react-flow-architect

安装量: 84
排名: #9450

安装

npx skills add https://github.com/sickn33/antigravity-awesome-skills --skill react-flow-architect
ReactFlow Architect
Build production-ready ReactFlow applications with hierarchical navigation, performance optimization, and advanced state management.
Quick Start
Create basic interactive graph:
import
ReactFlow
,
{
Node
,
Edge
}
from
"reactflow"
;
const
nodes
:
Node
[
]
=
[
{
id
:
"1"
,
position
:
{
x
:
0
,
y
:
0
}
,
data
:
{
label
:
"Node 1"
}
}
,
{
id
:
"2"
,
position
:
{
x
:
100
,
y
:
100
}
,
data
:
{
label
:
"Node 2"
}
}
,
]
;
const
edges
:
Edge
[
]
=
[
{
id
:
"e1-2"
,
source
:
"1"
,
target
:
"2"
}
]
;
export
default
function
Graph
(
)
{
return
<
ReactFlow
nodes
=
{
nodes
}
edges
=
{
edges
}
/>
;
}
Core Patterns
Hierarchical Tree Navigation
Build expandable/collapsible tree structures with parent-child relationships.
Node Schema
interface
TreeNode
extends
Node
{
data
:
{
label
:
string
;
level
:
number
;
hasChildren
:
boolean
;
isExpanded
:
boolean
;
childCount
:
number
;
category
:
"root"
|
"category"
|
"process"
|
"detail"
;
}
;
}
Incremental Node Building
const
buildVisibleNodes
=
useCallback
(
(
allNodes
:
TreeNode
[
]
,
expandedIds
:
Set
<
string
>
,
otherDeps
:
any
[
]
)
=>
{
const
visibleNodes
=
new
Map
<
string
,
TreeNode
>
(
)
;
const
visibleEdges
=
new
Map
<
string
,
TreeEdge
>
(
)
;
// Start with root nodes
const
rootNodes
=
allNodes
.
filter
(
(
n
)
=>
n
.
data
.
level
===
0
)
;
// Recursively add visible nodes
const
addVisibleChildren
=
(
node
:
TreeNode
)
=>
{
visibleNodes
.
set
(
node
.
id
,
node
)
;
if
(
expandedIds
.
has
(
node
.
id
)
)
{
const
children
=
allNodes
.
filter
(
(
n
)
=>
n
.
parentNode
===
node
.
id
)
;
children
.
forEach
(
(
child
)
=>
addVisibleChildren
(
child
)
)
;
}
}
;
rootNodes
.
forEach
(
(
root
)
=>
addVisibleChildren
(
root
)
)
;
return
{
nodes
:
Array
.
from
(
visibleNodes
.
values
(
)
)
,
edges
:
Array
.
from
(
visibleEdges
.
values
(
)
)
,
}
;
}
,
[
]
,
)
;
Performance Optimization
Handle large datasets with incremental rendering and memoization.
Incremental Rendering
const
useIncrementalGraph
=
(
allNodes
:
Node
[
]
,
allEdges
:
Edge
[
]
,
expandedList
:
string
[
]
,
)
=>
{
const
prevExpandedListRef
=
useRef
<
Set
<
string
>>
(
new
Set
(
)
)
;
const
prevOtherDepsRef
=
useRef
<
any
[
]
>
(
[
]
)
;
const
{
visibleNodes
,
visibleEdges
}
=
useMemo
(
(
)
=>
{
const
currentExpandedSet
=
new
Set
(
expandedList
)
;
const
prevExpandedSet
=
prevExpandedListRef
.
current
;
// Check if expanded list changed
const
expandedChanged
=
!
areSetsEqual
(
currentExpandedSet
,
prevExpandedSet
)
;
// Check if other dependencies changed
const
otherDepsChanged
=
!
arraysEqual
(
otherDeps
,
prevOtherDepsRef
.
current
)
;
if
(
expandedChanged
&&
!
otherDepsChanged
)
{
// Only expanded list changed - incremental update
return
buildIncrementalUpdate
(
cachedVisibleNodesRef
.
current
,
cachedVisibleEdgesRef
.
current
,
allNodes
,
allEdges
,
currentExpandedSet
,
prevExpandedSet
,
)
;
}
else
{
// Full rebuild needed
return
buildFullGraph
(
allNodes
,
allEdges
,
currentExpandedSet
)
;
}
}
,
[
allNodes
,
allEdges
,
expandedList
,
...
otherDeps
]
)
;
return
{
visibleNodes
,
visibleEdges
}
;
}
;
Memoization Patterns
// Memoize node components to prevent unnecessary re-renders
const
ProcessNode
=
memo
(
(
{
data
,
selected
}
:
NodeProps
)
=>
{
return
(
<
div className
=
{
`
process-node
${
selected
?
'selected'
:
''
}
`
}
>
{
data
.
label
}
<
/
div
>
)
;
}
,
(
prevProps
,
nextProps
)
=>
{
// Custom comparison function
return
(
prevProps
.
data
.
label
===
nextProps
.
data
.
label
&&
prevProps
.
selected
===
nextProps
.
selected
&&
prevProps
.
data
.
isExpanded
===
nextProps
.
data
.
isExpanded
)
;
}
)
;
// Memoize edge calculations
const
styledEdges
=
useMemo
(
(
)
=>
{
return
edges
.
map
(
edge
=>
(
{
...
edge
,
style
:
{
...
edge
.
style
,
strokeWidth
:
selectedEdgeId
===
edge
.
id
?
3
:
2
,
stroke
:
selectedEdgeId
===
edge
.
id
?
'#3b82f6'
:
'#94a3b8'
,
}
,
animated
:
selectedEdgeId
===
edge
.
id
,
}
)
)
;
}
,
[
edges
,
selectedEdgeId
]
)
;
State Management
Complex node/edge state patterns with undo/redo and persistence.
Reducer Pattern
type
GraphAction
=
|
{
type
:
"SELECT_NODE"
;
payload
:
string
}
|
{
type
:
"SELECT_EDGE"
;
payload
:
string
}
|
{
type
:
"TOGGLE_EXPAND"
;
payload
:
string
}
|
{
type
:
"UPDATE_NODES"
;
payload
:
Node
[
]
}
|
{
type
:
"UPDATE_EDGES"
;
payload
:
Edge
[
]
}
|
{
type
:
"UNDO"
}
|
{
type
:
"REDO"
}
;
const
graphReducer
=
(
state
:
GraphState
,
action
:
GraphAction
)
:
GraphState
=>
{
switch
(
action
.
type
)
{
case
"SELECT_NODE"
:
return
{
...
state
,
selectedNodeId
:
action
.
payload
,
selectedEdgeId
:
null
,
}
;
case
"TOGGLE_EXPAND"
:
const
newExpanded
=
new
Set
(
state
.
expandedNodeIds
)
;
if
(
newExpanded
.
has
(
action
.
payload
)
)
{
newExpanded
.
delete
(
action
.
payload
)
;
}
else
{
newExpanded
.
add
(
action
.
payload
)
;
}
return
{
...
state
,
expandedNodeIds
:
newExpanded
,
isDirty
:
true
,
}
;
default
:
return
state
;
}
}
;
History Management
const
useHistoryManager
=
(
state
:
GraphState
,
dispatch
:
Dispatch
<
GraphAction
>
,
)
=>
{
const
canUndo
=
state
.
historyIndex
>
0
;
const
canRedo
=
state
.
historyIndex
<
state
.
history
.
length
-
1
;
const
undo
=
useCallback
(
(
)
=>
{
if
(
canUndo
)
{
const
newIndex
=
state
.
historyIndex
-
1
;
const
historyEntry
=
state
.
history
[
newIndex
]
;
dispatch
(
{
type
:
"RESTORE_FROM_HISTORY"
,
payload
:
{
...
historyEntry
,
historyIndex
:
newIndex
,
}
,
}
)
;
}
}
,
[
canUndo
,
state
.
historyIndex
,
state
.
history
]
)
;
const
saveToHistory
=
useCallback
(
(
)
=>
{
dispatch
(
{
type
:
"SAVE_TO_HISTORY"
}
)
;
}
,
[
dispatch
]
)
;
return
{
canUndo
,
canRedo
,
undo
,
redo
,
saveToHistory
}
;
}
;
Advanced Features
Auto-Layout Integration
Integrate Dagre for automatic graph layout:
import
dagre
from
"dagre"
;
const
layoutOptions
=
{
rankdir
:
"TB"
,
// Top to Bottom
nodesep
:
100
,
// Node separation
ranksep
:
150
,
// Rank separation
marginx
:
50
,
marginy
:
50
,
edgesep
:
10
,
}
;
const
applyLayout
=
(
nodes
:
Node
[
]
,
edges
:
Edge
[
]
)
=>
{
const
g
=
new
dagre
.
graphlib
.
Graph
(
)
;
g
.
setGraph
(
layoutOptions
)
;
g
.
setDefaultEdgeLabel
(
(
)
=>
(
{
}
)
)
;
// Add nodes to graph
nodes
.
forEach
(
(
node
)
=>
{
g
.
setNode
(
node
.
id
,
{
width
:
200
,
height
:
100
}
)
;
}
)
;
// Add edges to graph
edges
.
forEach
(
(
edge
)
=>
{
g
.
setEdge
(
edge
.
source
,
edge
.
target
)
;
}
)
;
// Calculate layout
dagre
.
layout
(
g
)
;
// Apply positions
return
nodes
.
map
(
(
node
)
=>
(
{
...
node
,
position
:
{
x
:
g
.
node
(
node
.
id
)
.
x
-
100
,
y
:
g
.
node
(
node
.
id
)
.
y
-
50
,
}
,
}
)
)
;
}
;
// Debounce layout calculations
const
debouncedLayout
=
useMemo
(
(
)
=>
debounce
(
applyLayout
,
150
)
,
[
]
)
;
Focus Mode
Isolate selected nodes and their direct connections:
const
useFocusMode
=
(
selectedNodeId
:
string
,
allNodes
:
Node
[
]
,
allEdges
:
Edge
[
]
,
)
=>
{
return
useMemo
(
(
)
=>
{
if
(
!
selectedNodeId
)
return
{
nodes
:
allNodes
,
edges
:
allEdges
}
;
// Get direct connections
const
connectedNodeIds
=
new
Set
(
[
selectedNodeId
]
)
;
const
focusedEdges
:
Edge
[
]
=
[
]
;
allEdges
.
forEach
(
(
edge
)
=>
{
if
(
edge
.
source
===
selectedNodeId
||
edge
.
target
===
selectedNodeId
)
{
focusedEdges
.
push
(
edge
)
;
connectedNodeIds
.
add
(
edge
.
source
)
;
connectedNodeIds
.
add
(
edge
.
target
)
;
}
}
)
;
// Get connected nodes
const
focusedNodes
=
allNodes
.
filter
(
(
n
)
=>
connectedNodeIds
.
has
(
n
.
id
)
)
;
return
{
nodes
:
focusedNodes
,
edges
:
focusedEdges
}
;
}
,
[
selectedNodeId
,
allNodes
,
allEdges
]
)
;
}
;
// Smooth transitions for focus mode
const
focusModeStyles
=
{
transition
:
"all 0.3s ease-in-out"
,
opacity
:
isInFocus
?
1
:
0.3
,
filter
:
isInFocus
?
"none"
:
"blur(2px)"
,
}
;
Search Integration
Search and navigate to specific nodes:
const
searchNodes
=
useCallback
(
(
nodes
:
Node
[
]
,
query
:
string
)
=>
{
if
(
!
query
.
trim
(
)
)
return
[
]
;
const
lowerQuery
=
query
.
toLowerCase
(
)
;
return
nodes
.
filter
(
(
node
)
=>
node
.
data
.
label
.
toLowerCase
(
)
.
includes
(
lowerQuery
)
||
node
.
data
.
description
?.
toLowerCase
(
)
.
includes
(
lowerQuery
)
,
)
;
}
,
[
]
)
;
const
navigateToSearchResult
=
(
nodeId
:
string
)
=>
{
// Expand parent nodes
const
nodePath
=
calculateBreadcrumbPath
(
nodeId
,
allNodes
)
;
const
parentIds
=
nodePath
.
slice
(
0
,
-
1
)
.
map
(
(
n
)
=>
n
.
id
)
;
setExpandedIds
(
(
prev
)
=>
new
Set
(
[
...
prev
,
...
parentIds
]
)
)
;
setSelectedNodeId
(
nodeId
)
;
// Fit view to node
fitView
(
{
nodes
:
[
{
id
:
nodeId
}
]
,
duration
:
800
}
)
;
}
;
Performance Tools
Graph Performance Analyzer
Create a performance analysis script:
// scripts/graph-analyzer.js
class
GraphAnalyzer
{
analyzeCode
(
content
,
filePath
)
{
const
analysis
=
{
metrics
:
{
nodeCount
:
this
.
countNodes
(
content
)
,
edgeCount
:
this
.
countEdges
(
content
)
,
renderTime
:
this
.
estimateRenderTime
(
content
)
,
memoryUsage
:
this
.
estimateMemoryUsage
(
content
)
,
complexity
:
this
.
calculateComplexity
(
content
)
,
}
,
issues
:
[
]
,
optimizations
:
[
]
,
patterns
:
this
.
detectPatterns
(
content
)
,
}
;
// Detect performance issues
this
.
detectPerformanceIssues
(
analysis
)
;
// Suggest optimizations
this
.
suggestOptimizations
(
analysis
)
;
return
analysis
;
}
countNodes
(
content
)
{
const
nodePatterns
=
[
/
nodes:
\s
*
[
.
*?
]
/
gs
,
/
const
\s
+
\w
+
\s
*
=
\s
*
[
.
*?
id:
.
*?
position:
/
gs
,
]
;
let
totalCount
=
0
;
nodePatterns
.
forEach
(
(
pattern
)
=>
{
const
matches
=
content
.
match
(
pattern
)
;
if
(
matches
)
{
matches
.
forEach
(
(
match
)
=>
{
const
nodeMatches
=
match
.
match
(
/
id:
\s
*
[
'"`
]
[
^
'"`
]
+
[
'"`
]
/
g
)
;
if
(
nodeMatches
)
{
totalCount
+=
nodeMatches
.
length
;
}
}
)
;
}
}
)
;
return
totalCount
;
}
estimateRenderTime
(
content
)
{
const
nodeCount
=
this
.
countNodes
(
content
)
;
const
edgeCount
=
this
.
countEdges
(
content
)
;
// Base render time estimation (ms)
const
baseTime
=
5
;
const
nodeTime
=
nodeCount
*
0.1
;
const
edgeTime
=
edgeCount
*
0.05
;
return
baseTime
+
nodeTime
+
edgeTime
;
}
detectPerformanceIssues
(
analysis
)
{
const
{
metrics
}
=
analysis
;
if
(
metrics
.
nodeCount
>
500
)
{
analysis
.
issues
.
push
(
{
type
:
"HIGH_NODE_COUNT"
,
severity
:
"high"
,
message
:
`
Too many nodes (
${
metrics
.
nodeCount
}
). Consider virtualization.
`
,
suggestion
:
"Implement virtualization or reduce visible nodes"
,
}
)
;
}
if
(
metrics
.
renderTime
>
16
)
{
analysis
.
issues
.
push
(
{
type
:
"SLOW_RENDER"
,
severity
:
"high"
,
message
:
`
Render time (
${
metrics
.
renderTime
.
toFixed
(
2
)
}
ms) exceeds 60fps.
`
,
suggestion
:
"Optimize with memoization and incremental rendering"
,
}
)
;
}
}
}
Best Practices
Performance Guidelines
Use React.memo
for node components to prevent unnecessary re-renders
Implement virtualization
for graphs with 1000+ nodes
Debounce layout calculations
during rapid interactions
Use useCallback
for edge creation and manipulation functions
Implement proper TypeScript types
for nodes and edges
Memory Management
// Use Map for O(1) lookups instead of array.find
const
nodesById
=
useMemo
(
(
)
=>
new
Map
(
allNodes
.
map
(
(
n
)
=>
[
n
.
id
,
n
]
)
)
,
[
allNodes
]
,
)
;
// Cache layout results
const
layoutCacheRef
=
useRef
<
Map
<
string
,
Node
[
]
>>
(
new
Map
(
)
)
;
// Proper cleanup in useEffect
useEffect
(
(
)
=>
{
return
(
)
=>
{
// Clean up any lingering references
nodesMapRef
.
current
.
clear
(
)
;
edgesMapRef
.
current
.
clear
(
)
;
}
;
}
,
[
]
)
;
State Optimization
// Use useRef for objects that shouldn't trigger re-renders
const
autoSaveDataRef
=
useRef
(
{
nodes
:
[
]
,
edges
:
[
]
,
lastSaved
:
Date
.
now
(
)
,
}
)
;
// Update properties without breaking reference
const
updateAutoSaveData
=
(
newNodes
:
Node
[
]
,
newEdges
:
Edge
[
]
)
=>
{
autoSaveDataRef
.
current
.
nodes
=
newNodes
;
autoSaveDataRef
.
current
.
edges
=
newEdges
;
autoSaveDataRef
.
current
.
lastSaved
=
Date
.
now
(
)
;
}
;
Common Problems & Solutions
Performance Issues
Problem
Lag during node expansion
Solution
Implement incremental rendering with change detection
Problem
Memory usage increases over time
Solution
Proper cleanup in useEffect hooks and use WeakMap for temporary data
Layout Conflicts
Problem
Manual positioning conflicts with auto-layout
Solution
Use controlled positioning state and separate layout modes
Rendering Issues
Problem
Excessive re-renders
Solution
Use memo, useMemo, and useCallback with stable dependencies
Problem
Slow layout calculations
Solution
Debounce layout calculations and cache results Complete Example import React , { useState , useCallback , useMemo , useRef } from 'react' ; import ReactFlow , { Node , Edge , useReactFlow } from 'reactflow' ; import dagre from 'dagre' ; import { debounce } from 'lodash' ; interface GraphState { nodes : Node [ ] ; edges : Edge [ ] ; selectedNodeId : string | null ; expandedNodeIds : Set < string

; history : GraphState [ ] ; historyIndex : number ; } export default function InteractiveGraph ( ) { const [ state , setState ] = useState < GraphState

( { nodes : [ ] , edges : [ ] , selectedNodeId : null , expandedNodeIds : new Set ( ) , history : [ ] , historyIndex : 0 , } ) ; const { fitView } = useReactFlow ( ) ; const layoutCacheRef = useRef < Map < string , Node [ ]

( new Map ( ) ) ; // Memoized styled edges const styledEdges = useMemo ( ( ) => { return state . edges . map ( edge => ( { ... edge , style : { ... edge . style , strokeWidth : state . selectedNodeId === edge . source || state . selectedNodeId === edge . target ? 3 : 2 , stroke : state . selectedNodeId === edge . source || state . selectedNodeId === edge . target ? '#3b82f6' : '#94a3b8' , } , animated : state . selectedNodeId === edge . source || state . selectedNodeId === edge . target , } ) ) ; } , [ state . edges , state . selectedNodeId ] ) ; // Debounced layout calculation const debouncedLayout = useMemo ( ( ) => debounce ( ( nodes : Node [ ] , edges : Edge [ ] ) => { const cacheKey = generateLayoutCacheKey ( nodes , edges ) ; if ( layoutCacheRef . current . has ( cacheKey ) ) { return layoutCacheRef . current . get ( cacheKey ) ! ; } const layouted = applyDagreLayout ( nodes , edges ) ; layoutCacheRef . current . set ( cacheKey , layouted ) ; return layouted ; } , 150 ) , [ ] ) ; const handleNodeClick = useCallback ( ( event : React . MouseEvent , node : Node ) => { setState ( prev => ( { ... prev , selectedNodeId : node . id , } ) ) ; } , [ ] ) ; const handleToggleExpand = useCallback ( ( nodeId : string ) => { setState ( prev => { const newExpanded = new Set ( prev . expandedNodeIds ) ; if ( newExpanded . has ( nodeId ) ) { newExpanded . delete ( nodeId ) ; } else { newExpanded . add ( nodeId ) ; } return { ... prev , expandedNodeIds : newExpanded , } ; } ) ; } , [ ] ) ; return ( < ReactFlow nodes = { state . nodes } edges = { styledEdges } onNodeClick = { handleNodeClick } fitView /

) ; } This comprehensive skill provides everything needed to build production-ready ReactFlow applications with hierarchical navigation, performance optimization, and advanced state management patterns. When to Use This skill is applicable to execute the workflow or actions described in the overview.

返回排行榜