You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Big Calendar新增事件后不重新渲染问题求助

React Big Calendar 新增事件后不渲染的解决方案

问题根源

  1. props与本地状态不同步:你通过useEffect初始化tasks状态,但依赖数组为空,仅在组件挂载时执行一次。当父组件的props.data更新(比如新增任务),tasks不会自动同步,导致日历的events数组未包含新数据。
  2. 闭包导致状态快照问题:addMockTask中调用setTasks([...tasks, ...])时,tasks是函数创建时的快照,无法正确获取最新状态,进一步加剧数据不一致。
  3. 冗余状态维护:tasks状态完全可以由props.data实时转换得到,没必要单独维护,反而增加了数据不同步的风险。

修复方案

  1. 移除冗余的tasks状态:直接基于props.data实时转换任务为日历事件,无需本地状态中转。
  2. 优化节假日事件生成:用数组map替代forEach,代码更简洁高效。
  3. 修正endAccessor的moment操作:避免修改原始日期对象(moment是可变类型),改用.clone()创建副本后再操作。
  4. 简化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 21:30:22