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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 02:35:21