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

react-beautiful-dnd嵌套拖拽时setState前props被异常修改问题

嵌套拖拽跨分组状态同步异常根因
  • 核心问题是嵌套状态的浅拷贝未切断引用关联,违反React不可变数据原则
    之前做的props拷贝仅处理了最外层对象,内层Rank数组、Group对象在多个Overall Group间存在内存引用共享。处理Rank拖拽时使用splice等方法原地修改数组,直接改动了共享内存中的数据,不需要等setState调用,所有引用该内存地址的props/state都会立刻同步变更,这就是观察到「setState调用前props已经被修改」的直接原因。
  • 复用的onDragEnd方法未做更新范围隔离
    处理内层Rank拖拽时,逻辑没有限定仅修改当前激活的Overall Group下的对应数据,错误操作了跨分组共享的源数据,导致Rank排序改动同步到所有分组。
  • 之前尝试的两个排查方案未命中根因:组件key仅解决React组件复用问题,外层浅拷贝无法解决嵌套层级的引用共享问题,自然无法修复异常。
修复方案
  • 严格遵循不可变数据更新规则,所有状态修改操作禁止直接触碰原state/props对象,处理嵌套更新时逐层做拷贝:
    处理Rank排序时,依次拷贝overallGroups数组、当前激活的目标Overall Group对象、Rank所属的Group对象、Rank数组,在全新拷贝的对象上完成排序操作,从根本上避免修改共享引用。参考实现:
    const onDragEnd = (result) => {
      const { source, destination, type } = result;
      if (!destination) return;
      // 区分拖拽层级
      if (type === 'GROUP') {
        // 外层Group拖拽逻辑:仅更新当前激活分组的groups排序,全程不修改原对象
        const newGroups = Array.from(activeOverallGroup.groups);
        const [removed] = newGroups.splice(source.index, 1);
        newGroups.splice(destination.index, 0, removed);
        const newOverallGroups = overallGroups.map(g => {
          return g.id === activeGroupId ? {...g, groups: newGroups} : g;
        })
        setOverallGroups(newOverallGroups);
        return;
      }
      if (type === 'RANK') {
        // 内层Rank拖拽逻辑:逐层拷贝,仅修改当前激活分组下对应Group的ranks
        const newOverallGroups = overallGroups.map(overallGroup => {
          // 非当前激活的分组直接返回原对象,不做任何修改
          if (overallGroup.id !== activeOverallGroupId) return overallGroup;
          return {
            ...overallGroup,
            groups: overallGroup.groups.map(group => {
              // 非拖拽所属的Group直接返回原对象
              if (group.id !== source.droppableId) return group;
              // 拷贝Rank数组后再做排序操作
              const newRanks = Array.from(group.ranks);
              const [removed] = newRanks.splice(source.index, 1);
              newRanks.splice(destination.index, 0, removed);
              return {...group, ranks: newRanks};
            })
          }
        })
        setOverallGroups(newOverallGroups);
      }
    }
    
  • 从初始化源头切断引用共享:初始化每个Overall Group的groups、ranks数据时,不要复用同一个初始数组/对象变量,对初始嵌套数据做深拷贝后再赋值给对应分组,避免初始状态就存在引用共享。
  • 给不同层级的Droppable设置独立的type属性(外层Group类型设为GROUP,内层Rank类型设为RANK),配合onDragEnd中的类型判断,确保不同层级的拖拽逻辑互不干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 07:06:19