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

React中Modal状态更新导致FullCalendar重复拉取事件的解决方案

问题描述

使用FullCalendar拉取会议事件时,在下方的「Schedule Meeting」模态框中选择日期、时间(触发React setState操作),FullCalendar会重复调用事件拉取函数从后端获取数据。已知可以用useMemo解决该问题,但作为React新手不清楚具体实现方式。

解决方案

核心原因是:每次组件重渲染(比如模态框里的setState触发)时,events属性传入的匿名函数都会被重新创建,FullCalendar检测到这个函数变化,就会重新触发事件拉取。用useMemo缓存这个函数,就能避免不必要的重复执行。

步骤1:导入useMemo钩子

在组件顶部导入React的useMemo(如果还需要缓存其他回调函数,也可以一并导入useCallback):

import { useMemo, useCallback } from 'react';

步骤2:用useMemo缓存事件拉取函数

把原本直接写在events属性里的匿名函数抽出来,用useMemo包裹,同时指定依赖项——只有当依赖项变化时,才重新创建这个函数。

修改后的代码示例:

// 在组件return语句之前,定义缓存后的事件拉取函数
const fetchEvents = useMemo(() => {
  return function(info, successCallback, failureCallback) {
    var startDateTime = info.start;
    var endDateTime = info.end;

    startDateTime.setMilliseconds(0);
    endDateTime.setMilliseconds(0);

    startDateTime = startDateTime.toISOString();
    endDateTime = endDateTime.toISOString();
    
    startDateTime = startDateTime.substring(0, startDateTime.length - 5) + 'Z';
    endDateTime = endDateTime.substring(0, endDateTime.length - 5) + 'Z';

    const url = `http://localhost:8080/api/meeting/abhyasi/${props.profile.googleId}/${startDateTime}/${endDateTime}`;
    const headers = new Headers();

    headers.append('X-API-KEY', ",PRGojtX9`c4e24219-fb95-;'.4ed8392hfds';");

    const requestOptions = {
      method: 'GET',
      headers: headers
    };

    fetch(url, requestOptions)
    .then(response => {
      if (response.status === 404) {
        failureCallback();
      }
      return response.json();
    })
    .then(data => {
      console.log(data);
      var meeting_events = data.map((meeting) => {
        return {
          title: meeting.meetingAgenda,
          start: meeting.startDateTime,
          end: meeting.endDateTime,
          extendedProps: {
            startUrl: meeting.meetingStartUrl,
            joinUrl: meeting.meetingJoinUrl,
            meetingId: meeting.zoomMeetingId,
            meetingPassword: meeting.meetingPassword
          }
        }
      });
      successCallback(meeting_events);
    })
    .catch(error => {
      failureCallback(error);
    });
  }
}, [props.profile.googleId]); // 依赖项:仅当googleId变化时,才重新生成函数

// 在FullCalendar组件中使用缓存后的函数
<FullCalendar
  // 其他属性保持不变
  events={fetchEvents}
  // 其他属性保持不变
/>

额外优化:缓存eventDidMount回调

你的eventDidMount函数也存在同样的问题——每次重渲染都会重新创建,导致不必要的DOM操作。可以用useCallback(专门用于缓存函数)优化:

// 缓存eventDidMount的回调函数
const handleEventDidMount = useCallback((info) => {
  let start = info.event.start.toISOString();
  let end = info.event.end.toISOString();
  let current = new Date().toISOString();

  if(current < end) {
    var button = document.createElement('button');
    button.innerHTML = 'Delete Meeting';
    button.className = 'btn btn-primary btn-sm';
    button.classList.add('float-end');
    button.style.marginLeft = '10px';
    button.style.borderRadius = '5px';
    button.style.backgroundColor = '#dc3545';
    button.style.borderColor = '#dc3545';
    button.addEventListener('click', function() {
      deleteMeeting(info.event);
    });

    info.el.querySelector('.fc-list-event-title').appendChild(button);
  }

  if(start <= current && end >= current) {
    var button = document.createElement('button');
    button.innerHTML = 'Start';
    button.className = 'btn btn-primary btn-sm';
    button.classList.add('float-end');
    button.style.marginLeft = '10px';
    button.style.borderRadius = '5px';
    button.addEventListener('click', function() {
      window.open(info.event.extendedProps.startUrl);
    });

    info.el.querySelector('.fc-list-event-title').appendChild(button);
  }

  if(end > current) {
    var button = document.createElement('button');
    button.innerHTML = 'Info';
    button.className = 'btn btn-primary btn-sm';
    button.style.marginLeft = '10px';
    button.style.borderRadius = '5px';
    button.classList.add('float-end');
    button.addEventListener('click', function() {
      setEvent(info.event);
      presentInfoModal();
    });

    info.el.querySelector('.fc-list-event-title').appendChild(button);
  }
}, [deleteMeeting, setEvent, presentInfoModal]); // 加入函数用到的所有依赖项

// 在FullCalendar中使用缓存后的回调
<FullCalendar
  // 其他属性保持不变
  eventDidMount={handleEventDidMount}
  // 其他属性保持不变
/>

关键说明

  • useMemo/useCallback的第二个参数是依赖数组:只有数组里的值发生变化时,才会重新生成缓存的函数。确保把函数内部用到的所有外部变量、函数都加入数组,避免出现过时的闭包问题。
  • 完成上述修改后,模态框里的setState触发组件重渲染时,缓存的函数不会被重新创建,FullCalendar就不会重复调用事件拉取接口了。

内容的提问来源于stack exchange,提问作者Sahil Sahu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 21:44:58