React中useState数组更新后mouseUp无法访问最新状态问题
解决React日历组件mouseUp无法获取最新events状态的问题
问题根源
React的useState更新是异步的,且组件未重新渲染时,当前闭包中的state变量会保持创建闭包时的旧值。你的handleMouseUp是在handleMouseDown执行时创建的闭包,它捕获的是事件创建前的events状态,所以自然拿不到刚创建的事件。
另外代码里存在一个潜在bug:newEvent未用let/const声明,会成为全局变量,可能导致多事件操作时的冲突,需要先修正。
解决方案:用useRef跟踪最新状态
useRef的.current属性是可变的,不受闭包限制,能实时获取最新值。我们可以用它来跟踪最新的events数组,以及当前正在编辑的事件。
步骤1:添加ref变量
import { useState, useRef, useEffect } from 'react'; // 初始值建议用空数组,避免filter操作报错 const [events, setEvents] = useState([]); const [key, setKey] = useState(1000); // 跟踪最新的events数组 const latestEventsRef = useRef(events); // 跟踪当前正在拖动编辑的事件 const currentEditingEventRef = useRef(null);
步骤2:同步更新ref与events状态
用useEffect监听events变化,实时更新ref:
useEffect(() => { latestEventsRef.current = events; }, [events]);
步骤3:在事件创建时保存当前事件到ref
修改handleClick,把创建的新事件存入ref,同时修正全局变量问题:
const handleClick = (e) => { console.log("HandleClick..."); const eventsSameDateTime = events.filter(ev => { const sameGridRow = ev.gridRowStart === getRowForEvent(e); const sameGridColum = ev.gridcolumn === getColumnForEvent(e); return sameGridRow && sameGridColum; }); const count = eventsSameDateTime.length + 1; // 用let声明,避免全局变量 const newEvent = { date: null, gridRowStart: getRowForEvent(e), gridRowEnd: getRowForEvent(e)+4, gridcolumn: getColumnForEvent(e), title: "new", createDate: new Date(), zIndex: count, }; // 保存当前编辑的事件到ref currentEditingEventRef.current = newEvent; const newEvents = [...eventsSameDateTime, newEvent] .sort((a, b) => a.createDate.getTime() - b.createDate.getTime()) .map((ev, i) => { const newKey = key + i; ev.key = newKey; ev.zIndex = i + 1; ev.otherEvents = count; return ev; }); // 一次性更新key,避免多次调用setKey setKey(prev => prev + newEvents.length); setEvents((prevState) => { const oldEvents = prevState.filter(pEv => !newEvents.find(nEv => nEv.key === pEv.key)); return [...oldEvents, ...newEvents]; }); }
步骤4:在mouseUp时通过ref获取最新状态
修改handleMouseUp,用latestEventsRef获取最新的events数组:
const handleMouseUp = (e3) => { e3.preventDefault(); console.log("HandleMouseUP"); const removeMouseMoveAndDown = (e) => { e.removeEventListener("mousemove", handleMouseMove); e.removeEventListener("mouseup", handleMouseUp); }; if (isEvent(e1.target)) { console.log("Removing Event mouseUpDown"); removeMouseMoveAndDown(e1.target); } const eventContainer = findWeekElement(e1.target); console.log("Removing EventContainer mouseUpDown"); removeMouseMoveAndDown(eventContainer); // 通过ref获取最新的events const latestEvents = latestEventsRef.current; console.log("events on mouseUp: ", latestEvents); // 根据当前编辑事件的key查找最新状态 const existingEvent = latestEvents.find(ev => ev.key === currentEditingEventRef.current.key); if (existingEvent) { props.payload.setValue(JSON.stringify(createUiEventForEvent(existingEvent))); props.eventActions.onCreate(); } }
额外优化点
- 避免在
map循环中多次调用setKey,一次性计算最终key值再更新,减少不必要的状态更新。 - 把
events初始值从null改为[],防止初始状态下执行filter方法时报错。
内容的提问来源于stack exchange,提问作者user3722424
相关产品推荐
相关产品推荐

