React中显示模态框时如何阻止日历组件重渲染?
问题修复:日历创建事件时跳回当前周的解决方案
问题根源
点击日历时间范围创建事件时,showModal状态变更触发CalendarComponent重渲染,而日历的当前视图日期未被持久化,导致重渲染后回到初始的当前周。
具体修复步骤
1. 持久化日历的当前视图日期
在CalendarComponent中添加状态保存当前日历显示的起始日期,确保重渲染时保持当前视图:
export default function CalendarComponent({onTimeRangeSelected}) { const [events, setEvents] = useState([]); const [showModal, setShowModal] = useState(false); // 新增:保存当前日历的起始日期,初始化为当前周的第一天(周一) const [startDate, setStartDate] = useState(DayPilot.Date.today().firstDayOfWeek(1)); const calendarRef = useRef(null); // 补上原代码遗漏的ref定义 const getCalendar = () => { return calendarRef.current.control; } useEffect(() => { const getEvents = async() => { return await pb.collection('events').getFullList(); } getEvents().then(data => { const mappedEvents = data.map(event => ({ id: event.id, start: new Date(event.start), end: new Date(event.end) })); setEvents(mappedEvents); }) }, []) const createEvent = async(args) => { const dp = getCalendar(); const event = await pb.collection('events').create({ start: args.start, end: args.end }); dp.events.add({ id: event.id, start: args.start, end: args.end, }); } const handleTimeRangeSelected = async (args) => { setShowModal(true); // 直接设为true,避免状态切换混乱 await createEvent(args); // 等待事件创建完成,避免异步顺序问题 } return ( <div style={styles.wrap}> <div style={styles.left}> <DayPilotNavigator selectMode={"Week"} weekStarts = {1} showMonths={2} skipMonths={2} startDate={startDate} // 绑定状态 selectionDay={startDate} // 绑定状态 onTimeRangeSelected={ args => { setStartDate(args.day); // 更新状态 getCalendar().update({ startDate: args.day }); }} /> </div> <div style={styles.main}> <DayPilotCalendar events={events} // 修正原代码的allEvents为state中的events ref={calendarRef} viewType = {"Week"} weekStarts = {1} headerDateFormat = {'dddd d.M.yyyy'} durationBarVisible = {false} timeRangeSelectedHandling = {"Hold"} onTimeRangeSelected={handleTimeRangeSelected} startDate={startDate} // 绑定状态,确保重渲染时保持当前视图 /> </div> {showModal && <CreateEvent showModal={showModal} setShowModal={setShowModal}></CreateEvent>} </div> ); }
2. 优化模态框组件的渲染逻辑
- 用
React.memo包裹模态框组件,减少不必要的重渲染 - 将操作body类名的逻辑移到
useEffect中,避免每次渲染执行
import React, { useState, useEffect } from 'react'; // 用React.memo包裹,仅当props变化时重渲染 export default React.memo(function CreateEvent({showModal, setShowModal}) { const [lesson, setLesson] = useState(''); const [lessons, setLessons] = useState([]); const toggleModal = () => { setShowModal(!showModal); } // 移到useEffect中,仅在showModal变化时执行 useEffect(() => { if (showModal) { document.body.classList.add('active-modal'); } else { document.body.classList.remove('active-modal'); } // 组件卸载时清理class return () => { document.body.classList.remove('active-modal'); }; }, [showModal]) useEffect(() => { const getLessons = async() => { return await pb.collection('lessons').getFullList(); } getLessons().then(data => { setLessons(data); }) }, []) const handleLessonChange = (e) => { setLesson(e.target.value); } return( <> {showModal && ( <div className="modal"> <div className="overlay" onClick={toggleModal}></div> <div className="modal-content"> <input type="button" className="close-modal" onClick={toggleModal} value="X"></input> <label htmlFor="lesson">Předmět:</label> <select id="lesson" value={lesson} onChange={handleLessonChange}> {lessons.map((lessonItem) => { // 重命名变量避免冲突 return( <option key={lessonItem.id} value={lessonItem.name}>{lessonItem.name}</option> // 添加key属性,符合React规范 ) })} </select> </div> </div> )} </> ) })
3. 额外优化点
- 给模态框的
option元素添加key属性,避免React列表渲染警告 - 修正
handleTimeRangeSelected中setShowModal的逻辑,直接设为true而非取反,避免状态切换混乱 - 补上原代码遗漏的
calendarRef定义
内容的提问来源于stack exchange,提问作者Sabby
相关产品推荐
相关产品推荐

