Files
zui/PERFORMANCE.md
2026-03-08 20:53:34 +01:00

49 lines
3.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Performance notes
## Already in place
- **Throttled node updates during drag** `onNodesChange` merges changes and flushes at most once per animation frame (~60 fps) so dragging doesnt 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`, and `flowContextValue` are memoized so their references dont change every render.
- **Custom `nodePropsAreEqual`** Node components use `React.memo(..., nodePropsAreEqual)` so a node only re-renders when its own `id`, `data`, `width`, `height`, or `selected` change (e.g. dragging one node doesnt force others to re-render when the library keeps their props reference stable).
- **ConfigNode derived data** `incomingEdges`, `incomingIds`, and `connected*` are wrapped in `useMemo` so they arent 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 wont re-render on graph updates.
### 2. Avoid reading full `nodes`/`edges` where possible
React Flows 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; its 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** `NodeResizeControl` and `useResizeHeight` add work. If you dont need resize everywhere, make it optional or only on certain node types.
### 4. CSS
- **Containment** `BaseNode` already uses `contain: layout` where 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 `hidden` property (or similar) so only expanded nodes are rendered.
- **Virtualization** React Flow doesnt virtualize by default. For 100+ nodes, consider only rendering nodes in view (e.g. with `onlyRenderVisibleElements` if/when available in your version) or a custom viewport-based filter.
### 7. Build / runtime
- **Production build** Use `vite build` and 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.