performance
This commit is contained in:
68
src/App.tsx
68
src/App.tsx
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, { useCallback, useMemo, useRef } from 'react'
|
||||
import {
|
||||
ReactFlow,
|
||||
ReactFlowProvider,
|
||||
@@ -8,10 +8,12 @@ import {
|
||||
addEdge,
|
||||
useNodesState,
|
||||
useEdgesState,
|
||||
applyNodeChanges,
|
||||
type Node,
|
||||
type Edge,
|
||||
type Connection,
|
||||
type ColorMode,
|
||||
type NodeChange,
|
||||
} from '@xyflow/react'
|
||||
import ConfigNode from './components/graph/ConfigNode'
|
||||
import FunctionNode from './components/graph/FunctionNode'
|
||||
@@ -64,7 +66,7 @@ const initialEdges: Edge[] = [
|
||||
|
||||
export default function App() {
|
||||
const { theme, toggleTheme } = useTheme()
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes)
|
||||
const [nodes, setNodes, onNodesChangeBase] = useNodesState(initialNodes)
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
|
||||
const [rfInstance, setRfInstance] = React.useState<any | null>(null)
|
||||
const [renamingNodeId, setRenamingNodeId] = React.useState<string | null>(null)
|
||||
@@ -74,6 +76,36 @@ export default function App() {
|
||||
const lastClickRef = React.useRef<{ clientX: number; clientY: number } | null>(null)
|
||||
const [contextTarget, setContextTarget] = React.useState<null | { type: 'canvas'; clientX: number; clientY: number }>(null)
|
||||
|
||||
// Throttle node changes during drag: merge changes and flush at most once per animation frame to reduce re-renders
|
||||
const pendingChangesRef = useRef<NodeChange<Node>[]>([])
|
||||
const rafRef = useRef<number | null>(null)
|
||||
const onNodesChange = useCallback(
|
||||
(changes: NodeChange<Node>[]) => {
|
||||
if (changes.length === 0) return
|
||||
const pending = pendingChangesRef.current
|
||||
for (const c of changes) {
|
||||
const id = (c as { id?: string }).id
|
||||
if (id != null) {
|
||||
const i = pending.findIndex((p) => (p as { id?: string }).id === id)
|
||||
if (i >= 0) pending[i] = c
|
||||
else pending.push(c)
|
||||
} else {
|
||||
pending.push(c)
|
||||
}
|
||||
}
|
||||
if (rafRef.current === null) {
|
||||
rafRef.current = requestAnimationFrame(() => {
|
||||
rafRef.current = null
|
||||
const toApply = pendingChangesRef.current.splice(0, pendingChangesRef.current.length)
|
||||
if (toApply.length > 0) {
|
||||
setNodes((nds) => applyNodeChanges(toApply, nds))
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
[setNodes]
|
||||
)
|
||||
|
||||
const nodeTypes = React.useMemo(
|
||||
() => ({ config: ConfigNode, render: RenderingNode, variable: VariableNode, function: FunctionNode }),
|
||||
[]
|
||||
@@ -81,6 +113,8 @@ export default function App() {
|
||||
|
||||
const edgeTypes = React.useMemo(() => ({ animated: AnimatedEdge }), [])
|
||||
|
||||
const defaultEdgeOptions = React.useMemo(() => ({ type: 'animated' as const }), [])
|
||||
|
||||
const onConnect = React.useCallback(
|
||||
(params: Connection) => setEdges((eds) => addEdge(params, eds)),
|
||||
[setEdges]
|
||||
@@ -121,6 +155,31 @@ export default function App() {
|
||||
setRfInstance(instance)
|
||||
}, [])
|
||||
|
||||
const flowContextValue = useMemo(
|
||||
() => ({
|
||||
nodes,
|
||||
setNodes,
|
||||
edges,
|
||||
setEdges,
|
||||
renamingNodeId,
|
||||
setRenamingNodeId,
|
||||
connectionFrom,
|
||||
setConnectionFrom,
|
||||
isValidConnection,
|
||||
}),
|
||||
[
|
||||
nodes,
|
||||
setNodes,
|
||||
edges,
|
||||
setEdges,
|
||||
renamingNodeId,
|
||||
setRenamingNodeId,
|
||||
connectionFrom,
|
||||
setConnectionFrom,
|
||||
isValidConnection,
|
||||
]
|
||||
)
|
||||
|
||||
const onContextMenuCapture = React.useCallback((ev: React.MouseEvent) => {
|
||||
const target = ev.target as HTMLElement
|
||||
const nodeEl = target.closest('.react-flow__node')
|
||||
@@ -236,7 +295,7 @@ export default function App() {
|
||||
>
|
||||
{theme === 'dark' ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
|
||||
</Button>
|
||||
<FlowContext.Provider value={{ nodes, setNodes, edges, setEdges, renamingNodeId, setRenamingNodeId, connectionFrom, setConnectionFrom, isValidConnection }}>
|
||||
<FlowContext.Provider value={flowContextValue}>
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger asChild>
|
||||
<div style={{ width: '100%', height: '100%' }}>
|
||||
@@ -252,12 +311,13 @@ export default function App() {
|
||||
isValidConnection={isValidConnection}
|
||||
nodeTypes={nodeTypes}
|
||||
edgeTypes={edgeTypes}
|
||||
defaultEdgeOptions={{ type: 'animated' }}
|
||||
defaultEdgeOptions={defaultEdgeOptions}
|
||||
colorMode={theme as ColorMode}
|
||||
snapToGrid
|
||||
snapGrid={SNAP_GRID}
|
||||
fitView
|
||||
onInit={onInit}
|
||||
nodeDragThreshold={1}
|
||||
>
|
||||
<Background variant="dots" gap={20} />
|
||||
<Controls />
|
||||
|
||||
Reference in New Issue
Block a user