fix(apollo-react): improve canvas overlay performance [MST-11141]#819
Merged
Conversation
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
Dependency License Review
License distribution
Excluded packages
|
Contributor
📦 Dev Packages🧹 Dev packages cleaned up after PR close. Last updated: 2026-06-16 08:34:13 PT |
There was a problem hiding this comment.
Pull request overview
This PR targets canvas performance during frequent geometry updates (dragging, multi-select, overlay/edge rendering) by reducing unnecessary renders and recomputations in node toolbars, viewport overlays, and animated edges.
Changes:
- Avoid mounting node toolbar/overlay portal layers while dragging or when multiple nodes are selected.
- Reduce expensive recomputation for edge animation output during geometry-only updates by memoizing only when animation is active.
- Expand use of the position-insensitive node
React.memocomparator (and export it via canvas utils) to cut down re-renders driven solely by absolute position updates.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| packages/apollo-react/src/canvas/utils/index.ts | Re-exports nodePropsEqual utilities from the canvas utils barrel. |
| packages/apollo-react/src/canvas/components/StickyNoteNode/StickyNoteNode.tsx | Uses selection context + position-insensitive memo comparator; avoids rendering toolbar overlays in drag/multi-select states. |
| packages/apollo-react/src/canvas/components/NodeViewportOverlay.tsx | Narrows store subscription to node geometry only (via useStore + shallow) instead of re-rendering on broader internal node changes. |
| packages/apollo-react/src/canvas/components/LoopNode/LoopNode.tsx | Uses parentLookup to avoid O(n) scans; avoids mounting toolbar during drag/multi-select. |
| packages/apollo-react/src/canvas/components/Edges/shared/hooks/useExecutionEdge.ts | Prevents recomputing edge animation output when the edge is not animating (geometry-only updates). |
| packages/apollo-react/src/canvas/components/ButtonHandle/HandleButton.tsx | Skips rendering portal overlays when the button/label content is not visible. |
| packages/apollo-react/src/canvas/components/BaseNode/BaseNode.tsx | Avoids mounting toolbar during drag/multi-select (rather than mounting and hiding). |
Collaborator
|
@SreedharAvvari are you able to show a before vs. after with react-scan please. |
Contributor
Author
Added, thanks! |
55eceac to
874fe50
Compare
snuziale
approved these changes
Jun 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR reduces unnecessary React/render work in the canvas overlay and loop-container paths during high-frequency interactions like dragging, resizing, multi-selecting, and geometry-only updates. The main target is avoidable component work around viewport portals, hidden overlays, loop chrome, sticky notes, and edge animation output.
What is optimized
NodeViewportOverlaynow reads justx,y,width, andheightfrom the React Flow store with a selector andshallowequality. That avoids re-rendering overlay portals when unrelated internal node fields change.HandleButtonskips the viewport portal when neither the add button nor its label is visible.NodeToolbaralso returnsnullfor hidden toolbar instances that are rendered through the node overlay portal, so hidden toolbars do not keep portal wrappers and animation structure alive.successhandle still exists for edge connectivity, but it no longer renders the visibleSuccesslabel. This removes a portalized connected-handle label that was showing up during nested-loop drag profiling.BodyFrame,ResizeControls, andResizeCornerIndicatorsare memoized so static loop chrome does not rerender just because the loop shell rerenders. Resize hit targets remain mounted, so unselected loops can still be resized by hovering the resize controls.StoryInfoPanelis memoized so the story description panel does not rerender during canvas drag profiling.StickyNoteNodenow uses the position-insensitive node props comparator, so moving nodes does not force sticky note markdown, edit state, resize handles, toolbar config, and color picker UI to re-render unnecessarily.useExecutionEdgestill applies status colors for all execution and validation states, but only callsgetStatusAnimationwhen the edge status isInProgress. Completed, failed, and validation-colored edges no longer rebuild animation output on path-only geometry updates.parentLookupinstead of scanningstate.nodesto determine whether a loop has children, making that check a map lookup rather than a full node-list scan.nodePropsEqualis exported from canvas utils so other canvas nodes can reuse the same position-insensitive memoization path.Demo
NodeViewportOverlay/ viewport portal.NodeWrapperandEdgeWrapper.CanvasEdge,SequenceEdge,EdgePath, andEdgeArrowstill rerender when edge positions change because the SVG geometry has to move with the nodes.Before vs After
image.2026-06-14.at.11.52.41.PM.mov
image.2026-06-14.at.11.55.18.PM.mov
Expected impact
Validation
pnpm --filter @uipath/apollo-react exec vitest run src/canvas/utils/nodePropsEqual.test.tsxpnpm --filter @uipath/apollo-react exec vitest run src/canvas/components/LoopNode/LoopNode.test.tsxpnpm --filter @uipath/apollo-react exec tsc --noEmit --project tsconfig.json