pref: reduce the times of useNodes reRender (#28682)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Joel 2025-11-26 16:52:47 +08:00 committed by GitHub
parent d7010f582f
commit ef0e1031b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 55 additions and 25 deletions

View File

@ -1,7 +1,7 @@
'use client'
import { useCallback, useEffect, useMemo } from 'react'
import { useNodes } from 'reactflow'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { useNodesInteractions } from '@/app/components/workflow/hooks/use-nodes-interactions'
import type { CommonNodeType } from '@/app/components/workflow/types'
import { ragPipelineNodesAction } from '@/app/components/goto-anything/actions/rag-pipeline-nodes'

View File

@ -3,7 +3,7 @@ import {
useCallback,
useMemo,
} from 'react'
import { useEdges, useNodes } from 'reactflow'
import { useEdges } from 'reactflow'
import { RiApps2AddLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import {
@ -22,7 +22,6 @@ import AppPublisher from '@/app/components/app/app-publisher'
import { useFeatures } from '@/app/components/base/features/hooks'
import type {
CommonEdgeType,
CommonNodeType,
Node,
} from '@/app/components/workflow/types'
import {
@ -42,6 +41,7 @@ import { useIsChatMode } from '@/app/components/workflow/hooks'
import type { StartNodeType } from '@/app/components/workflow/nodes/start/types'
import { useProviderContext } from '@/context/provider-context'
import { Plan } from '@/app/components/billing/type'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
const FeaturesTrigger = () => {
const { t } = useTranslation()
@ -58,7 +58,7 @@ const FeaturesTrigger = () => {
const toolPublished = useStore(s => s.toolPublished)
const lastPublishedHasUserInput = useStore(s => s.lastPublishedHasUserInput)
const nodes = useNodes<CommonNodeType>()
const nodes = useNodes()
const hasWorkflowNodes = nodes.length > 0
const startNode = nodes.find(node => node.data.type === BlockEnum.Start)
const startVariables = (startNode as Node<StartNodeType>)?.data?.variables

View File

@ -9,7 +9,7 @@ import {
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
import { useNodes } from 'reactflow'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import type {
OffsetOptions,
Placement,

View File

@ -4,7 +4,7 @@ import {
useEffect,
useMemo,
} from 'react'
import { useNodes } from 'reactflow'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { useTranslation } from 'react-i18next'
import BlockIcon from '../block-icon'
import type { BlockEnum, CommonNodeType } from '../types'

View File

@ -5,7 +5,6 @@ import {
import { useTranslation } from 'react-i18next'
import {
useEdges,
useNodes,
} from 'reactflow'
import {
RiCloseLine,
@ -19,7 +18,6 @@ import {
import type { ChecklistItem } from '../hooks/use-checklist'
import type {
CommonEdgeType,
CommonNodeType,
} from '../types'
import cn from '@/utils/classnames'
import {
@ -32,7 +30,10 @@ import {
} from '@/app/components/base/icons/src/vender/line/general'
import { Warning } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
import { IconR } from '@/app/components/base/icons/src/vender/line/arrows'
import type { BlockEnum } from '../types'
import type {
BlockEnum,
} from '../types'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
type WorkflowChecklistProps = {
disabled: boolean
@ -42,8 +43,8 @@ const WorkflowChecklist = ({
}: WorkflowChecklistProps) => {
const { t } = useTranslation()
const [open, setOpen] = useState(false)
const nodes = useNodes<CommonNodeType>()
const edges = useEdges<CommonEdgeType>()
const nodes = useNodes()
const needWarningNodes = useChecklist(nodes, edges)
const { handleNodeSelect } = useNodesInteractions()

View File

@ -4,7 +4,7 @@ import {
useRef,
} from 'react'
import { useTranslation } from 'react-i18next'
import { useEdges, useNodes, useStoreApi } from 'reactflow'
import { useEdges, useStoreApi } from 'reactflow'
import type {
CommonEdgeType,
CommonNodeType,
@ -56,6 +56,7 @@ import {
} from '@/service/use-tools'
import { useStore as useAppStore } from '@/app/components/app/store'
import { AppModeEnum } from '@/types/app'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
export type ChecklistItem = {
id: string
@ -407,7 +408,7 @@ export const useChecklistBeforePublish = () => {
export const useWorkflowRunValidation = () => {
const { t } = useTranslation()
const nodes = useNodes<CommonNodeType>()
const nodes = useNodes()
const edges = useEdges<CommonEdgeType>()
const needWarningNodes = useChecklist(nodes, edges)
const { notify } = useToastContext()

View File

@ -1,5 +1,5 @@
import { useMemo } from 'react'
import { useNodes } from 'reactflow'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { useTranslation } from 'react-i18next'
import { BlockEnum, type CommonNodeType } from '../types'
import { getWorkflowEntryNode } from '../utils/workflow-entry'

View File

@ -18,6 +18,7 @@ import ReactFlow, {
ReactFlowProvider,
SelectionMode,
useEdgesState,
useNodes,
useNodesState,
useOnViewportChange,
useReactFlow,
@ -97,6 +98,7 @@ import {
useAllMCPTools,
useAllWorkflowTools,
} from '@/service/use-tools'
import { isEqual } from 'lodash-es'
const Confirm = dynamic(() => import('@/app/components/base/confirm'), {
ssr: false,
@ -167,7 +169,24 @@ export const Workflow: FC<WorkflowProps> = memo(({
setShowConfirm,
setControlPromptEditorRerenderKey,
setSyncWorkflowDraftHash,
setNodes: setNodesInStore,
} = workflowStore.getState()
const currentNodes = useNodes()
const setNodesOnlyChangeWithData = useCallback((nodes: Node[]) => {
const nodesData = nodes.map(node => ({
id: node.id,
data: node.data,
}))
const oldData = workflowStore.getState().nodes.map(node => ({
id: node.id,
data: node.data,
}))
if (!isEqual(oldData, nodesData))
setNodesInStore(nodes)
}, [setNodesInStore, workflowStore])
useEffect(() => {
setNodesOnlyChangeWithData(currentNodes as Node[])
}, [currentNodes, setNodesOnlyChangeWithData])
const {
handleSyncWorkflowDraft,
syncWorkflowDraftWhenPageClose,

View File

@ -4,7 +4,7 @@ import {
useRef,
} from 'react'
import { useClickAway } from 'ahooks'
import { useNodes } from 'reactflow'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import PanelOperatorPopup from './nodes/_base/components/panel-operator/panel-operator-popup'
import type { Node } from './types'
import { useStore } from './store'
@ -16,7 +16,6 @@ const NodeContextmenu = () => {
const { handleNodeContextmenuCancel, handlePaneContextmenuCancel } = usePanelInteractions()
const nodeMenu = useStore(s => s.nodeMenu)
const currentNode = nodes.find(node => node.id === nodeMenu?.nodeId) as Node
useEffect(() => {
if (nodeMenu)
handlePaneContextmenuCancel()

View File

@ -1,7 +1,5 @@
import { useCallback } from 'react'
import {
useNodes,
} from 'reactflow'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { uniqBy } from 'lodash-es'
import {
useIsChatMode,

View File

@ -1,6 +1,6 @@
import type { FC } from 'react'
import React from 'react'
import { useNodes } from 'reactflow'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { useTranslation } from 'react-i18next'
import type { AssignerNodeType } from './types'
import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'

View File

@ -1,6 +1,6 @@
import type { FC } from 'react'
import React from 'react'
import { useNodes } from 'reactflow'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { useTranslation } from 'react-i18next'
import type { DocExtractorNodeType } from './types'
import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'

View File

@ -3,7 +3,7 @@ import {
useMemo,
} from 'react'
import { useTranslation } from 'react-i18next'
import { useNodes } from 'reactflow'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { ComparisonOperator } from '../types'
import {
comparisonOperatorNotRequireValue,
@ -46,7 +46,7 @@ const ConditionValue = ({
if (Array.isArray(value)) // transfer method
return value[0]
if(value === true || value === false)
if (value === true || value === false)
return value ? 'True' : 'False'
return value.replace(/{{#([^#]*)#}}/g, (a, b) => {

View File

@ -1,6 +1,6 @@
import type { FC } from 'react'
import React from 'react'
import { useNodes } from 'reactflow'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { useTranslation } from 'react-i18next'
import type { ListFilterNodeType } from './types'
import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'

View File

@ -3,7 +3,7 @@ import {
useMemo,
} from 'react'
import { useTranslation } from 'react-i18next'
import { useNodes } from 'reactflow'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { useStore } from '../../../store'
import { BlockEnum } from '../../../types'
import type {

View File

@ -1,8 +1,9 @@
import { useCallback } from 'react'
import {
useNodes,
useStoreApi,
} from 'reactflow'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { uniqBy } from 'lodash-es'
import { produce } from 'immer'
import {

View File

@ -0,0 +1,7 @@
import {
useStore,
} from '@/app/components/workflow/store'
const useWorkflowNodes = () => useStore(s => s.nodes)
export default useWorkflowNodes

View File

@ -23,6 +23,8 @@ export type WorkflowDraftSliceShape = {
setIsSyncingWorkflowDraft: (isSyncingWorkflowDraft: boolean) => void
isWorkflowDataLoaded: boolean
setIsWorkflowDataLoaded: (loaded: boolean) => void
nodes: Node[]
setNodes: (nodes: Node[]) => void
}
export const createWorkflowDraftSlice: StateCreator<WorkflowDraftSliceShape> = set => ({
@ -37,4 +39,6 @@ export const createWorkflowDraftSlice: StateCreator<WorkflowDraftSliceShape> = s
setIsSyncingWorkflowDraft: isSyncingWorkflowDraft => set(() => ({ isSyncingWorkflowDraft })),
isWorkflowDataLoaded: false,
setIsWorkflowDataLoaded: loaded => set(() => ({ isWorkflowDataLoaded: loaded })),
nodes: [],
setNodes: nodes => set(() => ({ nodes })),
})