diff --git a/PERFORMANCE.md b/PERFORMANCE.md new file mode 100644 index 0000000..45b00b6 --- /dev/null +++ b/PERFORMANCE.md @@ -0,0 +1,48 @@ +# 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 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`, and `flowContextValue` are memoized so their references don’t 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 doesn’t 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 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 `