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
相关产品推荐
相关产品推荐

