ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

React Flow边缘丢失与错位问题:基于Hooks的状态管理重构实践

React Flow边缘丢失与错位问题:基于Hooks的状态管理重构实践 做流程编辑器最怕什么不是节点拖不动而是你辛辛苦苦拉好的连线一刷新、一切Tab、一拖某个节点它说没就没或者线头直接插进节点身体里。最近我把公司的 React Flow 流程编辑器彻底重构了一遍核心就一句话把节点和边缘的状态管理从组件里抽出来全部收进 Hooks 层。重构完以后边缘丢失和错位这两类问题从“隔三差五报一次bug”变成了“很久没再管过”。这篇文章我尽量把问题根源讲透再把 Hooks 重构的方案一步步拆开包含可以复制的代码和参数处理思路。用的例子基于xyflow/reactReact Flow v12 之后的包名如果你还在用 v11API 上稍微对照一下即可思路完全通用。适合正在做流程编排、图编辑器、低代码连线功能并且被“边缘不稳定”折磨过的人。1. 问题画像边缘丢失与错位的四种典型现场先说现象。边缘Edge在 React Flow 里就是节点之间的那根连线数据模型很简单一个source起始节点 id、一个target目标节点 id再加上两个 handle 的 id 就能画出来。但“简单”不代表“稳定”我整理了实际项目里反复出现的四类现场你先对号入座。1.1 现场一组件一重渲染连线直接消失最常见的情况是父组件里某个状态变了整个流程编辑区域被重新渲染结果节点还在边一根都不剩。你再检查代码发现edges数组里数据还在但 React Flow 就是显示不出来。这种通常是边缘数据根本没有传给 React Flow或者传了但 React Flow 在内部做数据合法性校验时把“失效”边缘丢掉了。React Flow 不会把所有你的edges都照单全收它会要求每条 edge 的source和target必须能对应到真实存在的节点对不上就直接丢。如果你在父组件渲染链条的某个地方把edges传丢了或者节点数组和边缘数组不是同一份数据源就会触发这种“数据还在、图面空了”的诡异状态。1.2 现场二节点增删后边缘指向了“不存在”的地方第二个高频场景是流程运行过程中动态增删节点。比如你删掉中间一个节点然后发现原本连到它后面的几条边全没了或者连到了完全错误的节点上。这个问题的根源往往是节点 id 不稳定。有人喜欢用数组下标当 id有人用Date.now()或者Math.random()更有人图省事直接在渲染函数里生成 edges。节点一增删id 一变边缘的source和target对不上了React Flow 只能做丢弃处理。这种情况不是 React Flow 的 bug纯粹是你给边缘提供了“虚假的引用地址”。1.3 现场三布局刷新后线还在但位置全错了第三类问题最让人头疼。线没丢但线的起点和终点偏移了线头扎在节点的左半边或者从节点上方穿过去看起来像是边缘层和节点层坐标没对齐。这类错位绝大多数和异步布局计算有关。我们的编辑器接入了自动布局算法dagre、elkjs 这类异步拿到新坐标后更新节点position但 React Flow 内部需要根据节点新坐标去重新计算边缘路径。如果更新时机不对比如节点才更新一半、或者节点尺寸还没测量完就去渲染边缘路径自然对不上。还有一种隐藏场景自定义节点内部结构发生了变化Handle 的数量或者位置变了但 React Flow 内部持有的节点信息还是旧的边缘起终点也就跟着错了。1.4 现场四Tab 切换或弹窗关闭后整张图状态清零还有一种“边缘丢失”是整体性的。流程编辑器放在 Modal 弹窗里或者放在 Tab 页里弹窗关掉再打开图全空了得重新加载一次数据。问题在于组件卸载时React Flow 内部的状态和你的节点、边缘数据一起被销毁了。React Flow 在非受控模式下会把节点位置、缩放、边缘路径都存在组件内部组件卸载等于把现场全部清空。如果数据没有持久化到上层状态看起来就是“边缘丢失”。其实边缘没丢是整张图的状态都丢了。2. 根源分析React Flow 边缘为什么这么“娇气”要解决问题光知道现象不够还得搞明白 React Flow 内部是怎么处理边缘的。在我看来边缘之所以容易出问题核心有三个原因。2.1 边缘是“挂靠”在节点两端 Handle 上的React Flow 的边缘本身没有坐标数据它依赖两侧节点的 Handle 位置来推算路径。渲染时系统内部会读取source节点上指定 Handle 的全局坐标再读取target节点上指定 Handle 的全局坐标然后在这两点之间画一条贝塞尔曲线。这意味着只要source或target引用的节点不存在边缘就画不出来。只要 Handle 的 id 找不到边缘就画不出来。只要节点坐标更新了但边缘层没有重新计算路径就是旧的。理解了这一点你会发现大部分边缘问题的根源都不是“边缘本身”而是它依赖的节点、Handle、坐标链路出了问题。用个不恰当但好懂的类比边缘是桥节点是桥墩桥墩歪了、塌了、被挪走了桥自然不可能还是那座桥。2.2 渲染层与数据层分离谁先谁后很容易出问题React Flow 组件在渲染时会做数据快照节点数组和边缘数组作为 props 传入后内部会维护一套基于传入数据的派生状态。如果你用的是非受控模式也就是节点和边缘只通过defaultNodes、defaultEdges传给组件之后完全靠内部维护那么数据的“真相”在 React Flow 内部你从外部拿不到如果中途又穿插了部分受控操作比如你通过setNodes改了位置但没有同时改边缘两边就会脱节。最典型的错误是同时用了两套真相来源一部分节点由外部 state 管理另一部分由 React Flow 内部状态管理边缘还挂在第三方状态里。这种设计下边缘丢失和错位几乎是必然结果因为任何一个状态源更新其他状态源都感知不到。2.3 Reactive Store 机制与受控状态的冲突React Flow 内部使用 zustand 这样的响应式 store 来管理视图状态缩放、偏移、选中态、hover 态。它会把用户传入的nodes和edges同步到 store 中。当用户拖拽节点时React Flow 会更新 store 里的节点坐标然后回调onNodesChange。问题在于这套机制有严格的时序要求。如果你在onNodesChange里对节点做了过滤、排序、id 重写等操作但没有保证边缘数据同步更新store 里的节点和边缘就可能在某一帧出现不一致。边缘路径计算是在渲染时根据 store 里的节点坐标和边缘引用同步进行的只要数据源短暂不一致就会出现“节点已经拖走了线还留在原地”的错位现象严重时边缘直接丢失。所以结论不是“React Flow 娇气”而是它严格依赖一条完整、一致、同步的数据链路。链路断在哪一环问题就出在哪一环。3. Hooks 重构方案把散落状态收拢成一层可控数据流我这次重构的整体思路是把所有节点和边缘状态收拢到一个自定义 Hook 里由这个 Hook 对外暴露稳定的操作函数任何组件通过函数来修改图数据而不是直接改数组。这套方案做下来边缘相关的问题明显少了很多。3.1 重构第一步用一个 useFlowGraph 统一收口节点和边缘我先定义了一个useFlowGraph自定义 Hook。它内部用useReducer管理节点和边缘对外只暴露state和一组操作函数。操作函数内部保证“节点变化时边缘里的引用同步一致”从机制上杜绝引用失效。import { useCallback, useReducer } from react; import type { Node, Edge } from xyflow/react; type FlowState { nodes: Node[]; edges: Edge[]; }; type FlowAction | { type: setNodes; nodes: Node[] } | { type: setEdges; edges: Edge[] } | { type: addNode; node: Node } | { type: removeNode; nodeId: string } | { type: addEdge; edge: Edge } | { type: removeEdge; edgeId: string }; function flowReducer(state: FlowState, action: FlowAction): FlowState { switch (action.type) { case setNodes: return { ...state, nodes: action.nodes }; case setEdges: return { ...state, edges: action.edges }; case addNode: return { ...state, nodes: [...state.nodes, action.node] }; case removeNode: { const nodes state.nodes.filter((n) n.id ! action.nodeId); const edges state.edges.filter( (e) e.source ! action.nodeId e.target ! action.nodeId ); return { nodes, edges }; } case addEdge: return { ...state, edges: dedupeEdges([...state.edges, action.edge]) }; case removeEdge: return { ...state, edges: state.edges.filter((e) e.id ! action.edgeId) }; default: return state; } } export function useFlowGraph(initialNodes: Node[], initialEdges: Edge[]) { const [state, dispatch] useReducer(flowReducer, { nodes: initialNodes, edges: initialEdges, }); const addNode useCallback((node: Node) dispatch({ type: addNode, node }), []); const removeNode useCallback((nodeId: string) dispatch({ type: removeNode, nodeId }), []); const addEdge useCallback((edge: Edge) dispatch({ type: addEdge, edge }), []); const removeEdge useCallback((edgeId: string) dispatch({ type: removeEdge, edgeId }), []); const setNodes useCallback((nodes: Node[]) dispatch({ type: setNodes, nodes }), []); const setEdges useCallback((edges: Edge[]) dispatch({ type: setEdges, edges }), []); return { nodes: state.nodes, edges: state.edges, addNode, removeNode, addEdge, removeEdge, setNodes, setEdges, }; }这个 Hook 看起来简单但解决了两个关键问题。第一所有状态修改都收敛到一个 reducer 里没有绕道修改。以前大家习惯在组件里直接setNodes(prev [...prev])然后在另一个地方又setEdges(...)两个数组完全靠程序员自觉保持同步。重构后删除节点这一个动作会同时清理边缘不用再到处找哪里漏了。第二addEdge内部接了一层dedupeEdges去重。这一点从机制上防止了重复边缘让人头痛的问题下面展开说。3.2 重构第二步边缘数据去重与稳定性设计边缘丢失的对立面是边缘重复。你拖一次连接线回调里addEdge被触发两次结果同一对节点之间冒出两根完全重叠的线视觉上像一根线但选中时明显能看出两条。这种问题非常常见根源就是边缘数据没有做唯一性校验。我在重构里写了一个去重函数把“相同连接关系”的边缘合并成一条。判断逻辑不是只看source和target还要把 Handle id 也带进去因为同一个节点可能有多个连接点从 A 节点的输出 1 连到 B 节点的输入 2和从 A 节点的输出 2 连到 B 节点的输入 1不是同一条边。function dedupeEdges(edges: Edge[]): Edge[] { const seen new Setstring(); return edges.filter((edge) { // 自环没有意义直接干掉 if (edge.source edge.target) return false; const key ${edge.source}-${edge.target}-${edge.sourceHandle ?? null}-${edge.targetHandle ?? null}; if (seen.has(key)) return false; seen.add(key); return true; }); }这套去重逻辑我把它放在 reducer 的addEdge分支里而不是放在显式的按钮触发处。原因是 React Flow 的onConnect回调在某些版本和交互模式下可能触发多次只有在校验入口集中兜底才能彻底防住。另外要强调一个容易被忽略的点边缘的 id 也要稳定。如果 edge 的 id 在每次渲染时都用Math.random()生成React Flow 内部的动画、选中态、删除操作都会出问题。重构后我给每条新边缘生成一个确定性的 id规则是${source}-${target}-${Date.now()}这样既稳定又不至于在多条同源同目标边缘时冲突。3.3 重构第三步实例与视图状态分离布局计算放到 Hooks 里第三部分是解决“错位”问题的关键。错位的本质是节点坐标变了但视图层没有正确回写。我这里的做法是把 React Flow 的实例操作和节点数据处理彻底分开节点数据、边缘数据由useFlowGraph管理。React Flow 实例screenToFlowPosition、fitView、setCenter这些视图方法由useReactFlow在组件内部获取。异步布局计算的代码放在单独的useAutoLayoutHook 里不写在组件体内。其中最重要的一点是布局计算完成后必须通过setNodes用全量新数组替换旧数组绝对不能原地修改节点的position属性。React Flow 对节点数组的更新依赖引用变化你直接改node.position.x xxstore 里检测不到变化边缘路径就不会重新计算。我的useAutoLayout大概是这样的结构import { useEffect } from react; import { useReactFlow, type Node, type Edge } from xyflow/react; type LayoutResult { nodes: Node[]; edges: Edge[]; }; // 你需要实现一个 layoutGraph内部调 dagre/elkjs或者自己写分层算法 async function layoutGraph(nodes: Node[], edges: Edge[]): PromiseLayoutResult { // 这里是关键布局算法需要知道节点宽高所以要先取到 nodes 的 measured 尺寸 const layoutNodes nodes.map((node) ({ ...node, width: node.measured?.width ?? 200, height: node.measured?.height ?? 60, })); // 调 dagre 或 elkjs返回带 position 的新节点数组 return { nodes: layoutNodes, edges }; } export function useAutoLayout(nodes: Node[], edges: Edge[], setNodes: (nodes: Node[]) void) { const { fitView } useReactFlow(); useEffect(() { let cancelled false; layoutGraph(nodes, edges).then(({ nodes: layoutedNodes }) { if (cancelled) return; setNodes(layoutedNodes); // 等渲染一帧后再 fitView确保边缘路径已经按新坐标计算完 requestAnimationFrame(() fitView({ padding: 0.2, duration: 200 })); }); return () { cancelled true; }; }, [edges.length]); // 注意不要把 nodes 整个塞进依赖数组否则拖拽节点也会触发重排 }这里的依赖数组选择很关键。我刻意只依赖edges.length这样自动布局只会在连接关系变化时触发节点拖动不会引发整图重排。如果某些业务要求“拖动后重新布局”你可以自己调整为依赖其他信号但我建议至少不要每次都重算否则用户体验会非常差。布局后错位还有一个隐蔽但很常见的原因布局算法拿不到节点真实尺寸。React Flow 里节点尺寸是渲染后测量得到的放在node.measured.width和node.measured.height上。如果自定义节点的内容会动态变化比如折叠展开测量值可能过期。我在layoutGraph里做了兜底拿不到就按 200x60 算同时建议自定义节点组件里尽可能给外层容器一个固定宽度基线减少测量波动。4. 实操过程从命令式补丁到声明式数据流的完整改造画了这么多理论具体改造长什么样我把重构前后的核心代码都贴出来你可以对比着看差别。4.1 改造前的问题代码bad smell这是我在线上抓到过的问题版本典型的边丢边错边漏export function FlowEditor() { const [nodes, setNodes] useStateNode[](initialNodes); const [edges, setEdges] useStateEdge[](initialEdges); const reactFlowInstance useRefReactFlowInstance | null(null); // 隐患1用数组索引作为节点 id const addNode () { const newId node-${nodes.length}; setNodes((prev) [...prev, { id: newId, position: { x: 100, y: 100 } }]); }; // 隐患2删除节点时只删节点不清理关联边缘 const removeNode (id: string) { setNodes((prev) prev.filter((n) n.id ! id)); }; // 隐患3onConnect 里直接 setEdges没有任何稳定性和去重处理 const onConnect useCallback( (params: Connection) { const newEdge { ...params, id: edge-${Date.now()} }; setEdges((prev) [...prev, newEdge]); }, [setEdges] ); // 隐患4手动布局直接改 node 对象属性 const handleLayout () { nodes.forEach((node) { node.position { x: node.position.x, y: node.position.y 100 }; }); setNodes([...nodes]); // 数组是新的但 node 引用没变React Flow 检测不到节点坐标变化 }; return ( ReactFlow nodes{nodes} edges{edges} onConnect{onConnect} onInit{(instance) { reactFlowInstance.current instance; }} / ); }这段代码包含了我在生产环境看到过的绝大部分坏味道。数组下标当 id、删除节点不联动删边、connect 时直接 push、布局时原地改对象。等节点数量一多、操作一频繁边缘丢失和错位自然就来了。最隐蔽的是“隐患4”那个写法。setNodes([...nodes])确实创建了新数组但数组里的node对象还是原来那些引用。React Flow 做浅比较时发现数组变了会走更新流程但节点对象本身引用没变内部的position变更不会被 React 触发重新渲染。结果就是你在nodes变量里看到的坐标是对的但界面上节点和边缘纹丝不动等你手动随便点一下页面强制重绘后才跳过去。这种“看起来边缘错位”的 bug 最难排查因为你打日志看数据都是对的。4.2 重构后的核心实现重构后组件本身瘦身成了很薄的一层import { useFlowGraph, useAutoLayout } from ./hooks/useFlowGraph; import { ReactFlow, ReactFlowProvider, Controls, Background } from xyflow/react; import xyflow/react/dist/style.css; import type { Node, Edge } from xyflow/react; const initialNodes: Node[] [ { id: start, type: custom, position: { x: 0, y: 0 }, data: { label: 开始 } }, ]; const initialEdges: Edge[] []; function FlowCanvas() { const { nodes, edges, setNodes, setEdges, addEdge, removeNode, } useFlowGraph(initialNodes, initialEdges); // 自动布局只在边缘数量变化时触发 useAutoLayout(nodes, edges, setNodes); const onConnect useCallback( (connection: Connection) { const edge: Edge { ...connection, id: edge-${connection.source}-${connection.target}-${Date.now()}, }; addEdge(edge); }, [addEdge] ); const onNodesChange useCallback((changes: NodeChange[]) { setNodes((prev) applyNodeChanges(changes, prev)); }, [setNodes]); const onEdgesChange useCallback((changes: EdgeChange[]) { setEdges((prev) applyEdgeChanges(changes, prev)); }, [setEdges]); const onNodeDoubleClick useCallback( (_: React.MouseEvent, node: Node) { removeNode(node.id); }, [removeNode] ); return ( div style{{ width: 100%, height: 800px }} ReactFlow nodes{nodes} edges{edges} onConnect{onConnect} onNodesChange{onNodesChange} onEdgesChange{onEdgesChange} onNodeDoubleClick{onNodeDoubleClick} fitView proOptions{{ hideAttribution: true }} Controls / Background / /ReactFlow /div ); } export default function FlowEditor() { return ( ReactFlowProvider FlowCanvas / /ReactFlowProvider ); }注意几个关键点。第一useFlowGraph返回了setNodes和setEdges这个看起来像是把旧的“直接 setState”又放出来了但意义完全不同。这里的setNodes实际上 dispatch 到 reducer并且 reducer 里的setNodes分支只负责整体替换数组没有其他副作用。关键作用是把“状态修改入口”收拢到一个稳定引用上组件每次渲染拿到的都是同一个函数引用避免因为函数地址变化导致 React Flow 内部重复订阅或意外重新渲染。第二onNodesChange和onEdgesChange我都用官方提供的applyNodeChanges和applyEdgeChanges去处理。这两个工具函数是 React Flow 的核心v11 和 v12 都有负责把拖拽、选中、删除等交互事件翻译成最终的节点/边缘数组。很多边缘错位问题其实是因为有人在这里只对 nodes 做了处理没处理dimensions的变化或者对 edges 的变化忽略了source/target关联信息。直接用官方工具函数是最省心的做法。第三我把自定义节点的宽高前移到了一个独立设计里。在自定义节点组件中根节点统一用一个minWidth和minHeight兜底且 Handle 的位置尽量两侧对齐不放在内容中部。这样可以保证即使某些内容还没渲染出来React Flow 测量到的节点尺寸也不至于偏到影响边缘路径。4.3 布局回写与边缘坐标更新的参数处理自动布局最容易踩的坑是“布局算完节点坐标更新了但边缘路径没有重新计算”。这不是 React Flow 的 bug而是布局库返回结果没有正确写入节点数据。我之前在 production 环境遇过一个特别迷惑的问题用 elkjs 做分层布局布局完以后节点位置是对的边缘却是从旧位置连出来的整个图画成了蜘蛛网。查了半天发现elkjs 返回的节点数组顺序是打乱的而且返回的坐标是绝对坐标但我在合并数据时用了“按 id 原位覆盖”的逻辑有些节点 id 匹配失败导致部分节点坐标没更新。React Flow 发现约一半的节点坐标没变就只重算了部分边缘路径整体看起来就是错位的。解决方法是布局结果合并时必须全量生成新节点对象并给每个节点显式赋值positionconst layoutedNodes layouted.map((item) { const originalNode nodes.find((n) n.id item.id); return { ...originalNode, position: { x: item.x, y: item.y }, } as Node; });这里必须...originalNode展开而不是originalNode本身目的就是创建新的对象引用让 React Flow 内部能感知到“这个节点的数据变了”。同时要确保position是{ x, y }结构i 不能传成{ left: x, top: y }React Flow 内部是按position.x/position.y读取的字段名对不上它也不会报错只会表现得像边缘错位。这个坑我帮同事排查过前端工具类框架的字段约定就是这样错了不提示只能靠文档和经验对照。另一个参数处理细节是fitView的时机。布局完成后别立刻调用fitView最好等一帧。因为节点position更新后React Flow 要经过一次渲染才能计算新的边缘路径和整体包围盒此时立刻 fitView 会拿到旧的包围盒坐标。我在代码里用requestAnimationFrame包了一层实测下来基本没再遇到过布局后边缘偏移出视野或者整体缩放比例不对的问题。5. 常见问题排查与避坑实录最后这部分是我最想写的因为很多问题是文档里搜不到的只能靠踩坑。5.1 常见问题速查表我把重构过程中遇到的和身边同事遇到的典型问题整理成一个速查表碰到类似现象可以先来这里对照。现象根本原因解决方向切 Tab 回来图是空的组件卸载导致 React Flow 内部状态丢失把节点/边缘状态提升到 Provider 或全局 store而不是放在编辑器组件内部拖拽节点后边缘路径闪烁/错位onNodesChange没有用applyNodeChanges正确处理统一走官方工具函数不要自己写变更逻辑删除中间节点后多条边消失节点 id 不稳定/边缘引用了已删除节点用稳定 idUUID 或自增业务 id删除节点时联动清理边缘布局后边缘路径还是旧位置布局结果没有生成新对象引用或position字段名错误全量创建新节点对象显式赋值position: { x, y }同一对节点出现重复连线连接回调触发了多次或边缘没有去重在addEdge/reducer 入口做去重自定义节点内部变化后 Handle 错位Handle 数量或位置变化但 React Flow 没感知使用useUpdateNodeInternals或在节点尺寸变化后触发节点数据更新边缘坐标看着对但渲染偏了容器尺寸不对或 viewport 初始化时机不对确认容器有明确宽高fitView延后到渲染完成后再调用连线时弹出“连接无效”但数据已写入source/target Handle 类型或数量不匹配检查自定义节点 Handle 的typesource/target和id是否和边缘一致5.2 好几个容易忽略的细节第一个细节是node.measured的使用时机。React Flow v12 的节点对象上有一个measured字段保存了渲染后的实际宽高。但这个字段是在节点真正渲染后才会更新初始状态是undefined。如果你在nodes的初始值里就想去读node.measured.width拿到的会是空。正确的做法是在自定义节点的data回调里指定初始widthstyle或者像我在layoutGraph里做的那样给一个默认值兜底。第二个细节是onConnect回调的触发次数。React Flow 的连线交互用户从源 Handle 拖到目标 Handle正常情况下onConnect只触发一次。但如果你的画布里有多个 ReactFlowProvider或者组件被挂载了多层事件冒泡可能导致回调触发多次。去重逻辑放 reducer 入口而不是调用处就是为了兜住这种“你以为只调一次实际调了两次”的情况。第三个细节是关于边缘的animated属性。很多人喜欢给边缘加流动动画让流程方向更明显。但如果你在布局后更新节点坐标且动画开着边缘状态会有短暂的重绘闪烁。这不是大问题但如果你敏感布局期间可以先把animated置为 false布局完成后再恢复。第四个细节是删除节点的确认。我用onNodeDoubleClick做删除但生产环境里误触导致删掉一整个子图的问题发生过好多次。后来我把删除逻辑改了删除前先把关联的节点和边缘做一个确认动作如果子图太大就弹窗确认。这不影响边缘稳定性但能避免用户一把把所有连线都删光之后来骂“线全丢了”。有些“边缘丢失”其实是人删的只是用户自己没意识到。第五个细节是关于defaultEdgeOptions。如果项目里所有链接线都是同一种类型curved、step、straight可以在 React Flow 组件上统一配置defaultEdgeOptions避免每个addEdge时都要写一遍样式字段。这个配置还能统一type和markerEnd减少数据字段遗漏导致边缘渲染成默认直线的情况。第六个细节是自定义节点里的 Handle 必须设置id尤其是节点上有多个输入输出时。如果 Handle 不设置 idReact Flow 内部用默认值null作为标识当节点有多个 source Handle 时onConnect返回的sourceHandle会全是null导致你根本无法区分连线是从哪个口拉出来的。重构后我在所有自定义节点里显式给 Handle 设置id并且确保和边缘数据里的sourceHandle完全一致。这一步做不好边缘会画到错误的连接点上看起来就是“错位”。6. 写在最后的一点体会这套 Hooks 重构方案上线到现在已经跑了半年多最直观的变化是群里关于“线不见了”“线画歪了”的反馈基本清零。我自己的体会是React Flow 本身的状态管理机制设计得挺严谨大部分边缘问题都是使用者的数据流没理顺导致框架内部的依赖链条断裂。如果你现在也在被边缘问题折磨我建议你先别急着加补丁花半天时间把节点和边缘的数据流完整梳理一遍谁在什么时机创建节点、谁在什么时机创建边缘、删除节点时有没有清理边缘、布局算法返回的数据是不是真的把每个节点都生成了新引用。把这几个问题回答清楚了八成的问题都能自己定位到。最后再分享一个小技巧我在开发环境给useFlowGraph的 reducer 加了一个 dev 日志每次 dispatch 都打印动作类型和当前节点/边缘数量。排查“边缘莫名丢失”的问题时这个日志能帮你瞬间定位是哪一次操作导致的。把状态变更日志打出来很多 bug 的复现和定位会快很多。
返回列表