在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> ))}
为什么这个方案有效?
- 函数式更新保证状态最新:每次调用
setFcn时,传入的函数会接收当前最新的prevArray状态,两次更新(删除、添加)会依次基于最新状态修改,不会出现覆盖问题。 - 避免了旧状态快照依赖:不再依赖渲染时的
array变量,完全基于React内部维护的最新状态进行修改。 - 无需flushSync:彻底摆脱了
flushSync带来的渲染警告,符合React的状态更新机制。
额外注意事项
- 确保key的唯一性:
ReactSortable组件和内部的列表项都必须使用唯一的key(推荐用数据的id而非索引),避免React渲染时出现复用错误。 - 数组克隆规范:使用
[...prevArray]创建新数组,而不是{...array}(虽然后者对数组也生效,但前者更符合数组操作的语义,避免混淆)。 - group属性一致性:所有跨拖拽的
ReactSortable必须设置相同的group属性,确保拖拽交互正常触发。
内容的提问来源于stack exchange,提问作者Power_tile
相关产品推荐
相关产品推荐

