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

在react-sortable中更新二维数组时避免使用flushSync的方案

解决react-sortable多列跨拖拽时元素丢失及flushSync警告问题

核心问题根源

你的问题本质是状态更新依赖了渲染时的旧快照:跨列表拖拽会触发两次状态更新(源列表删除元素、目标列表添加元素),但原来的setArrayRow函数使用的是组件渲染时的array变量(旧状态),两次更新基于同一旧状态修改,导致后一次更新覆盖前一次,最终元素丢失。而flushSync只是强行同步更新的临时方案,会触发React的渲染警告。

最优解决方案:使用函数式状态更新

React的setState支持函数式更新,可以获取到最新的状态快照,彻底避免旧状态覆盖的问题,同时不需要依赖flushSync。

修正后的setArrayRow函数

type Dispatch<A> = (value: A) => void;

function setArrayRow(setFcn: Dispatch<Item[][]>, idx: number): Dispatch<Item[]> {
  return (row: Item[]) => {
    // 使用函数式更新,基于最新的prevArray修改
    setFcn(prevArray => {
      // 创建新数组,避免直接修改原状态
      const newArray = [...prevArray];
      // 替换指定索引的行
      newArray[idx] = row;
      return newArray;
    });
  };
}

组件中使用方式

const [array, setArray] = useState<Item[][]>(init_value);

// 渲染多列可拖拽列表
{indices.map((rowIdx) => (
  <ReactSortable 
    key={rowIdx} // 给ReactSortable添加唯一key
    list={array[rowIdx]} 
    setList={setArrayRow(setArray, rowIdx)} 
    group="courses"
  >
    {array[rowIdx].map(item => (
      <div key={item.id}>{item.name}</div> // 列表项必须用唯一id作为key
    ))}
  </ReactSortable>
))}

为什么这个方案有效?

  1. 函数式更新保证状态最新:每次调用setFcn时,传入的函数会接收当前最新的prevArray状态,两次更新(删除、添加)会依次基于最新状态修改,不会出现覆盖问题。
  2. 避免了旧状态快照依赖:不再依赖渲染时的array变量,完全基于React内部维护的最新状态进行修改。
  3. 无需flushSync:彻底摆脱了flushSync带来的渲染警告,符合React的状态更新机制。

额外注意事项

  • 确保key的唯一性:ReactSortable组件和内部的列表项都必须使用唯一的key(推荐用数据的id而非索引),避免React渲染时出现复用错误。
  • 数组克隆规范:使用[...prevArray]创建新数组,而不是{...array}(虽然后者对数组也生效,但前者更符合数组操作的语义,避免混淆)。
  • group属性一致性:所有跨拖拽的ReactSortable必须设置相同的group属性,确保拖拽交互正常触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 19:22:51