3.5 KiB
Performance notes
Already in place
- Throttled node updates during drag –
onNodesChangemerges changes and flushes at most once per animation frame (~60 fps) so dragging doesn’t trigger hundreds of re-renders per second. nodeDragThreshold={1}– Avoids treating small pointer moves as drags and reduces noisy change events.- Memoized React Flow props –
nodeTypes,edgeTypes,defaultEdgeOptions, andflowContextValueare memoized so their references don’t change every render. - Custom
nodePropsAreEqual– Node components useReact.memo(..., nodePropsAreEqual)so a node only re-renders when its ownid,data,width,height, orselectedchange (e.g. dragging one node doesn’t force others to re-render when the library keeps their props reference stable). - ConfigNode derived data –
incomingEdges,incomingIds, andconnected*are wrapped inuseMemoso they aren’t recomputed on every render.
Further improvements you can try
1. Split context (medium effort)
Right now every component that uses useContext(FlowContext) re-renders whenever nodes or edges change. You can split into:
- FlowDataContext –
nodes,edges(changes often). - FlowActionsContext –
setNodes,setEdges,isValidConnection, etc. (stable).
Components that only need actions (e.g. menus, buttons) subscribe to FlowActionsContext and won’t re-render on graph updates.
2. Avoid reading full nodes/edges where possible
React Flow’s docs recommend not depending on the full nodes/edges arrays when you only need a small slice (e.g. “selected ids”). If you add features like selection state, keep that in a separate store or state (e.g. selectedNodeIds) and have components depend on that instead of filtering nodes everywhere.
3. Lighter node content
- CodeMirror – Config and function nodes use CodeMirror; it’s heavy. Options: lazy-initialize the editor (mount only when the node is focused or visible), or use a plain
<textarea>for very small/simple flows. - Resize –
NodeResizeControlanduseResizeHeightadd work. If you don’t need resize everywhere, make it optional or only on certain node types.
4. CSS
- Containment –
BaseNodealready usescontain: layoutwhere dimensions are set. Keeping layout/paint contained per node helps. - Simpler styles – For many nodes, avoid heavy shadows, blur, or complex animations on the node container; they can cost a lot during pan/zoom/drag.
5. RenderingNode pipeline
The render node runs Nunjucks + Kroki on signatures (config, edges, variables, functions). It already avoids re-running when irrelevant data changes. If you add more inputs, keep them in the signature pattern so the effect only runs when something that affects the result actually changes.
6. Large graphs
- Hide/collapse – For big trees, use the
hiddenproperty (or similar) so only expanded nodes are rendered. - Virtualization – React Flow doesn’t virtualize by default. For 100+ nodes, consider only rendering nodes in view (e.g. with
onlyRenderVisibleElementsif/when available in your version) or a custom viewport-based filter.
7. Build / runtime
- Production build – Use
vite buildand test with the production build; React and Vite are much faster without dev mode and source maps. - React DevTools Profiler – Record while dragging or editing to see which components re-render and how often; that will guide where to add more memoization or split context.