React Big Calendar新增事件后不重新渲染问题求助
React Big Calendar 新增事件后不渲染的解决方案
问题根源
- props与本地状态不同步:你通过
useEffect初始化tasks状态,但依赖数组为空,仅在组件挂载时执行一次。当父组件的props.data更新(比如新增任务),tasks不会自动同步,导致日历的events数组未包含新数据。 - 闭包导致状态快照问题:
addMockTask中调用setTasks([...tasks, ...])时,tasks是函数创建时的快照,无法正确获取最新状态,进一步加剧数据不一致。 - 冗余状态维护:
tasks状态完全可以由props.data实时转换得到,没必要单独维护,反而增加了数据不同步的风险。
修复方案
- 移除冗余的
tasks状态:直接基于props.data实时转换任务为日历事件,无需本地状态中转。 - 优化节假日事件生成:用数组
map替代forEach,代码更简洁高效。 - 修正
endAccessor的moment操作:避免修改原始日期对象(moment是可变类型),改用.clone()创建副本后再操作。 - 简化
addMockTask逻辑:仅需调用父组件的modifyData更新数据源,组件会自动根据新的props.data重新计算事件并触发渲染。
修改后的完整代码
import { useState, useEffect } from 'react'; import { Calendar, momentLocalizer } from 'react-big-calendar'; import moment from 'moment'; import 'react-big-calendar/lib/css/react-big-calendar.css'; import './calendar.css' const localizer = momentLocalizer(moment) export default function TaskSnapCalendar(props) { const [holidays, setHoliday] = useState([]); const HOLIDAY_API_URL = 'https://canada-holidays.ca/api/v1/holidays'; // 获取节假日数据 useEffect(() => { fetch(HOLIDAY_API_URL) .then(res => res.json()) .then(data => setHoliday(data.holidays)) }, []) // 转换节假日为日历事件 const holidayEvents = holidays .filter(holiday => holiday.federal) .map(holiday => ({ title: holiday.nameEn, start: moment(holiday.date).toDate(), end: moment(holiday.date).toDate(), allDay: true, type: 'holiday', color: '#D71313' })); // 转换props中的任务为日历事件(实时计算,无需本地状态) function taskToEvent(task) { return { title: task.title, start: moment(task.start).toDate(), end: moment(task.due).toDate(), allDay: !task.due.includes('T'), } } const taskEvents = props.data.map(taskToEvent); // 合并所有事件 const events = [...holidayEvents, ...taskEvents]; // 模拟添加任务 const dummyTask = { "id": "200", "title": "DUMMY TASK", "status": "ongoing", "assigned_to": "DUMMY", "start": "2023-08-24", "due": "2022-08-25", "label": "DUMMY", "description": "" } const addMockTask = () => { props.modifyData([...props.data, dummyTask]) } return <div className='content'> <Calendar className='main-calendar' localizer={localizer} events={events} startAccessor="start" // 修正moment操作,避免修改原始对象 endAccessor={event => event.allDay ? moment(event.end).clone().add(1, 'days').toDate() : event.end } showMultiDayTimes={true} eventPropGetter={event => ({ style: { backgroundColor: event.color } })} /> <button onClick={addMockTask}>TEST</button> </div> }
额外说明
当其他页面修改父组件数据后,只要父组件正确更新data并传递给TaskSnapCalendar,组件会自动重新计算taskEvents和events,触发日历重新渲染。移除本地tasks状态后,彻底避免了props与本地状态不同步的问题,代码更简洁可靠。
内容的提问来源于stack exchange,提问作者TonyKL
相关产品推荐
相关产品推荐

