fix: 修复覆盖图谱卸载竞态导致的 g6 崩溃(getTransformInstance)

d3-force 力导向布局是异步多帧模拟。布局未跑完就切走图谱标签→组件卸载
graph.destroy() 把 context 清空,飞行中的布局落地时 transformDataAfterLayout
读 context.transform.getTransformInstance() → undefined 崩溃。
- applyData 渲染前判 graph.destroyed;render() 挂 .catch 吞掉 teardown 竞态的
  拒绝(已销毁静默,未销毁仍打日志),不再冒泡成运行时错误。
- 卸载先 stopLayout() 再 destroy(),缩短竞态窗口。
This commit is contained in:
Autumn-27 committed 2026-08-18 09:15:42 -04:00
1 parent 3633ae78f4
commit d901543aba
1 file changed
+13 -2
@@ -507,9 +507,14 @@ function GraphInner({ taskId }: { taskId: string }) {
const applyData = React.useCallback(() => {
const graph = graphRef.current;
if (!graph) return;
if (!graph || graph.destroyed) return;
graph.setData(gDataRef.current);
void graph.render();
// render() 异步跑 d3-force 布局;若组件在布局落地前被卸载/销毁,g6 会在已清空的
// context 上访问 transform 抛错(见 runtime/layout transformDataAfterLayout)。这是纯
// teardown 竞态,吞掉它,不影响功能;真正的渲染错误(图未销毁)仍打日志。
void graph.render().catch((err) => {
if (!graph.destroyed) console.error("[coverage-graph] render:", err);
});
}, []);
// 建图(一次)。动态 import 避开 SSR/静态导出期的 window 依赖。
@@ -579,6 +584,12 @@ function GraphInner({ taskId }: { taskId: string }) {
})();
return () => {
destroyed = true;
// 先停布局再销毁:尽量缩短"布局在飞、context 被清空"的竞态窗口。
try {
graph?.stopLayout();
} catch {
/* 图可能尚未建成或已无布局上下文 */
}
graph?.destroy();
graphRef.current = null;
};