mirror of
https://github.com/langgenius/dify.git
synced 2026-01-28 19:14:46 +00:00
fix(workflow)!: add mounted guard to prevent ReactFlow operations after unmount
When switching from graph view to skill view during an active preview run, SSE callbacks continue executing and attempt to update ReactFlow node/edge states. This could cause errors since the component is unmounted. Add optional `isMountedRef` parameter to `useNodesInteractionsWithoutSync` and `useEdgesInteractionsWithoutSync` hooks. When provided, operations are skipped if the component has unmounted, preventing potential errors while allowing the SSE connection to continue running in the background. BREAKING CHANGE: `useNodesInteractionsWithoutSync` and `useEdgesInteractionsWithoutSync` now accept an optional `isMountedRef` parameter. Existing callers are unaffected as the parameter is optional.
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
import type { RefObject } from 'react'
|
||||
import { produce } from 'immer'
|
||||
import { useCallback } from 'react'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
|
||||
export const useEdgesInteractionsWithoutSync = () => {
|
||||
export const useEdgesInteractionsWithoutSync = (isMountedRef?: RefObject<boolean>) => {
|
||||
const store = useStoreApi()
|
||||
|
||||
const handleEdgeCancelRunningStatus = useCallback(() => {
|
||||
if (isMountedRef && isMountedRef.current === false)
|
||||
return
|
||||
|
||||
const {
|
||||
edges,
|
||||
setEdges,
|
||||
@@ -19,7 +23,7 @@ export const useEdgesInteractionsWithoutSync = () => {
|
||||
})
|
||||
})
|
||||
setEdges(newEdges)
|
||||
}, [store])
|
||||
}, [store, isMountedRef])
|
||||
|
||||
return {
|
||||
handleEdgeCancelRunningStatus,
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import type { RefObject } from 'react'
|
||||
import { produce } from 'immer'
|
||||
import { useCallback } from 'react'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
import { NodeRunningStatus } from '../types'
|
||||
|
||||
export const useNodesInteractionsWithoutSync = () => {
|
||||
export const useNodesInteractionsWithoutSync = (isMountedRef?: RefObject<boolean>) => {
|
||||
const store = useStoreApi()
|
||||
|
||||
const handleNodeCancelRunningStatus = useCallback(() => {
|
||||
if (isMountedRef && isMountedRef.current === false)
|
||||
return
|
||||
|
||||
const {
|
||||
getNodes,
|
||||
setNodes,
|
||||
@@ -20,9 +24,12 @@ export const useNodesInteractionsWithoutSync = () => {
|
||||
})
|
||||
})
|
||||
setNodes(newNodes)
|
||||
}, [store])
|
||||
}, [store, isMountedRef])
|
||||
|
||||
const handleCancelAllNodeSuccessStatus = useCallback(() => {
|
||||
if (isMountedRef && isMountedRef.current === false)
|
||||
return
|
||||
|
||||
const {
|
||||
getNodes,
|
||||
setNodes,
|
||||
@@ -36,9 +43,12 @@ export const useNodesInteractionsWithoutSync = () => {
|
||||
})
|
||||
})
|
||||
setNodes(newNodes)
|
||||
}, [store])
|
||||
}, [store, isMountedRef])
|
||||
|
||||
const handleCancelNodeSuccessStatus = useCallback((nodeId: string) => {
|
||||
if (isMountedRef && isMountedRef.current === false)
|
||||
return
|
||||
|
||||
const {
|
||||
getNodes,
|
||||
setNodes,
|
||||
@@ -52,7 +62,7 @@ export const useNodesInteractionsWithoutSync = () => {
|
||||
}
|
||||
})
|
||||
setNodes(newNodes)
|
||||
}, [store])
|
||||
}, [store, isMountedRef])
|
||||
|
||||
return {
|
||||
handleNodeCancelRunningStatus,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { DEFAULT_ITER_TIMES, DEFAULT_LOOP_TIMES } from '../../../constants'
|
||||
import { useEdgesInteractionsWithoutSync } from '../../../hooks/use-edges-interactions-without-sync'
|
||||
import { useNodesInteractionsWithoutSync } from '../../../hooks/use-nodes-interactions-without-sync'
|
||||
@@ -19,8 +19,14 @@ export function useChatFlowControl({
|
||||
const setHasStopResponded = useStore(s => s.setHasStopResponded)
|
||||
const setSuggestedQuestionsAbortController = useStore(s => s.setSuggestedQuestionsAbortController)
|
||||
const invalidateRun = useStore(s => s.invalidateRun)
|
||||
const { handleNodeCancelRunningStatus } = useNodesInteractionsWithoutSync()
|
||||
const { handleEdgeCancelRunningStatus } = useEdgesInteractionsWithoutSync()
|
||||
|
||||
const isMountedRef = useRef(true)
|
||||
useEffect(() => () => {
|
||||
isMountedRef.current = false
|
||||
}, [])
|
||||
|
||||
const { handleNodeCancelRunningStatus } = useNodesInteractionsWithoutSync(isMountedRef)
|
||||
const { handleEdgeCancelRunningStatus } = useEdgesInteractionsWithoutSync(isMountedRef)
|
||||
|
||||
const { setIterTimes, setLoopTimes } = workflowStore.getState()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user