dify/web/app/components/workflow/hooks/use-collaborative-workflow.ts
hjlarry 2f7336b5d9 fix knowledge retrieval node add freeze
Knowledge Retrieval panel effects depend on setInputs. In the collaboration branch the callback chain started changing identity on every render, so adding this node could repeatedly write node data and freeze the browser.

Stabilize the collaborative workflow API returned by useCollaborativeWorkflow and keep useNodeCrud.setInputs stable while forwarding to the latest updater. Add a regression test for the stable setInputs contract.
2026-04-13 16:59:58 +08:00

86 lines
2.3 KiB
TypeScript

import type { Edge, Node } from '../types'
import { useCallback, useMemo } from 'react'
import { useStoreApi } from 'reactflow'
import { collaborationManager } from '../collaboration/core/collaboration-manager'
const sanitizeNodeForBroadcast = (node: Node): Node => {
if (!node.data)
return node
if (!Object.prototype.hasOwnProperty.call(node.data, 'selected'))
return node
const sanitizedData = { ...node.data }
delete (sanitizedData as Record<string, unknown>).selected
return {
...node,
data: sanitizedData,
}
}
const sanitizeEdgeForBroadcast = (edge: Edge): Edge => {
if (!edge.data)
return edge
if (!Object.prototype.hasOwnProperty.call(edge.data, '_connectedNodeIsSelected'))
return edge
const sanitizedData = { ...edge.data }
delete (sanitizedData as Record<string, unknown>)._connectedNodeIsSelected
return {
...edge,
data: sanitizedData,
}
}
export const useCollaborativeWorkflow = () => {
const store = useStoreApi()
const { setNodes: collabSetNodes, setEdges: collabSetEdges } = collaborationManager
const setNodes = useCallback((newNodes: Node[], shouldBroadcast: boolean = true, source = 'use-collaborative-workflow:setNodes') => {
const { getNodes, setNodes: reactFlowSetNodes } = store.getState()
if (shouldBroadcast) {
const oldNodes = getNodes()
collabSetNodes(
oldNodes.map(sanitizeNodeForBroadcast),
newNodes.map(sanitizeNodeForBroadcast),
source,
)
}
reactFlowSetNodes(newNodes)
}, [store, collabSetNodes])
const setEdges = useCallback((newEdges: Edge[], shouldBroadcast: boolean = true) => {
const { edges, setEdges: reactFlowSetEdges } = store.getState()
if (shouldBroadcast) {
collabSetEdges(
edges.map(sanitizeEdgeForBroadcast),
newEdges.map(sanitizeEdgeForBroadcast),
)
}
reactFlowSetEdges(newEdges)
}, [store, collabSetEdges])
const collaborativeStore = useCallback(() => {
const state = store.getState()
return {
nodes: state.getNodes(),
edges: state.edges,
setNodes,
setEdges,
}
}, [store, setNodes, setEdges])
return useMemo(() => ({
getState: collaborativeStore,
setNodes,
setEdges,
}), [collaborativeStore, setEdges, setNodes])
}