React Flow v10 拖动单个节点连带其他节点移动异常问题
React Flow 10 拖拽单节点连带多节点移动异常排查
问题表现
升级至React Flow 10版本后,偶现异常现象:移动单个节点时,会连带拖拽多个其他节点一同移动。
问题效果演示:
相关核心代码
const onNodesChange = useCallback( (changes) => { if (nodesToShowRef.current && edgesToShowRef.current) { setElementsToShow(applyNodeChanges(changes, nodesToShowRef.current), edgesToShowRef.current) } }, [setElementsToShow] ); <ReactFlow ref={ref => mapRef.current = ref} onClick={(e) => { // 省略业务逻辑 }} minZoom={0.1} edgeTypes={edgeTypes} nodeTypes={nodeTypes} connectionLineComponent={FloatingConnectionLine} nodesDraggable={true} nodesConnectable={true} nodes={nodesToShow} edges={edgesToShow} fitView attributionPosition="top-left" noWheelClassName="no-wheel" selectNodesOnDrag={false} elementsSelectable={false} onNodesChange={onNodesChange} onNodeClick={(ev, node) => { ev.stopPropagation(); // 省略业务逻辑 }} onEdgeClick={(ev, edge) => { ev.stopPropagation(); // 省略业务逻辑 }} onEdgeUpdate={onEdgeUpdate} onConnect={onConnect} onNodeContextMenu={(event, node) => { event.stopPropagation(); event.preventDefault(); // 省略业务逻辑 }} onEdgeContextMenu={(event, edge) => { event.stopPropagation(); event.preventDefault(); // 省略业务逻辑 }} onContextMenu={onMapContextMenu} onNodeDragStop={onNodeDragStop} >
异常原因
该问题是onNodesChange回调的实现逻辑错误导致的,和React Flow 10本身的版本特性无关:
- React Flow 10拖拽节点时,内部会临时给当前拖拽节点打上
dragging: true的状态标记,拖拽过程中、拖拽结束后,会通过changes回调队列下发位置更新、标记清除的变更事件,需要调用applyNodeChanges把这些变更应用到最新的节点数据上,才能保证状态一致。 - 现有实现中,
applyNodeChanges的计算基准一直是nodesToShowRef.current中存储的初始/旧节点数据,每次状态更新后没有把最新的节点数据同步回该ref,导致之前拖拽过的节点的dragging: true标记无法被正确清除,长期残留在节点状态中。 - 当残留的
dragging: true标记累积到多个节点后,下次触发任意节点的拖拽操作时,React Flow内部会把所有带该标记的节点判定为待拖拽节点,就会出现拖一个节点、带多个节点一起移动的偶现现象。
修复方案
二选一即可解决问题:
- 方案1:移除ref依赖,用函数式状态更新保证变更计算的基准永远是最新的节点数据:
const onNodesChange = useCallback( (changes) => { setElementsToShow(prev => { const [prevNodes, prevEdges] = prev; return [applyNodeChanges(changes, prevNodes), prevEdges] }) }, [setElementsToShow] );
- 方案2:如果业务场景必须用ref存储节点和边数据,每次
setElementsToShow执行完成后,必须把最新的节点列表同步回nodesToShowRef.current,确保ref中存储的永远是最新状态,避免旧标记残留。
内容的提问来源于stack exchange,提问作者neomib
相关产品推荐
相关产品推荐

