Skip to content

Commit 291ad64

Browse files
nieaoclaude
andcommitted
fix(canvas): 渲染前清理孤儿 parentNode 防 React Flow 崩溃
React Flow 11 在 child.parentNode 找不到时直接 throw "Parent node X not found" 整个画布崩 (用户图 37 报告 ErrorBoundary, 被迫切房间号绕开)。 yjs 同步顺序 / undo 撤销 / 老房间残留都可能造成短暂 parent-child 不一致。 KnowledgeCanvas 渲染前 sanitize: dangling parentNode 直接清掉, 孤儿 退化成普通根节点继续渲染, 不影响数据修复 (yjs 后续 sync 到 parent 后 渲染恢复正常)。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9de86c6 commit 291ad64

1 file changed

Lines changed: 21 additions & 1 deletion

File tree

src/components/canvas/KnowledgeCanvas.jsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ function QuickAddMenu({ x, y, onSelect, onClose }) {
268268

269269
// 画布内部组件
270270
function KnowledgeCanvasInner({
271-
nodes = [],
271+
nodes: rawNodes = [],
272272
edges = [],
273273
onNodesChange,
274274
onEdgesChange,
@@ -281,6 +281,26 @@ function KnowledgeCanvasInner({
281281
showMiniMap = true,
282282
children,
283283
}) {
284+
// === 渲染前 sanitize: 检测 parentNode 引用不存在的节点 ===
285+
// React Flow 11 在 child.parentNode 找不到时会直接 throw "Parent node X not found"
286+
// 整个画布崩 (用户报告 ErrorBoundary, 图 37)。这里清掉 dangling parentNode,
287+
// 让孤儿子节点退化成普通根节点继续渲染 — 协作场景下 yjs 同步顺序 / undo 都可能造成短暂不一致。
288+
const nodes = useMemo(() => {
289+
const idSet = new Set(rawNodes.map((n) => n.id))
290+
let dirty = false
291+
const cleaned = rawNodes.map((n) => {
292+
if (n.parentNode && !idSet.has(n.parentNode)) {
293+
dirty = true
294+
// eslint-disable-next-line no-unused-vars
295+
const { parentNode, extent, ...rest } = n
296+
return rest
297+
}
298+
return n
299+
})
300+
if (dirty) console.warn('[KnowledgeCanvas] 清理了孤儿 parentNode 引用, count =', cleaned.length)
301+
return cleaned
302+
}, [rawNodes])
303+
284304
const reactFlowInstance = useReactFlow()
285305
const reactFlowWrapper = useRef(null)
286306
const [isPanMode, setIsPanMode] = useState(false)

0 commit comments

Comments
 (0)