Skip to content

perf(calm-hub-ui): memoize visualiser node/edge components and cut hover re-allocations - #3125

Open
aamanrebello wants to merge 1 commit into
finos:mainfrom
aamanrebello:perf/memoize-visualizer-nodes
Open

aamanrebello wants to merge 1 commit into
finos:mainfrom
aamanrebello:perf/memoize-visualizer-nodes

Conversation

@aamanrebello

@aamanrebello aamanrebello commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

Description

Human Comment: Just asked Claude to look at Calm Hub UI and identify opportunities to optimize latency. Also asked it to investigate risks - this optimization seemed very low risk (although you will probably only see any real latency difference on large complicated graphs). In essence, this PR prevents unnecessary re-renders when hovering over nodes or edges in the ReactFlow graph.

Coding Assistant Comment: Wraps the visualiser's CustomNode, SystemGroupNode, and FloatingEdge components in React.memo, and changes the node hover handlers (useGraphInteractions.ts) to only allocate a new node object when its zIndex value actually changes.

Before this change, hovering any node caused every node component in the diagram to re-render, because the hover handlers rebuilt the whole node array on every mouseenter/mouseleave — giving every node a new object reference regardless of whether its z-index actually changed. React.memo alone cannot fix this; it needs stable prop references to skip a re-render. This change fixes both together: the hover handlers now only reallocate the node(s) whose z-index truly changed, so the memoized components correctly skip re-rendering everything else.

No behavior change — only render frequency. Hover, click, and drag interactions render identically; diagram output is unchanged.

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📚 Documentation update
  • 🎨 Code style/formatting changes
  • ♻️ Refactoring (no functional changes)
  • ⚡ Performance improvements
  • ✅ Test additions or updates
  • 🔧 Chore (maintenance, dependencies, CI, etc.)

Affected Components

  • CLI (cli/)
  • Schema (calm/)
  • CALM AI (calm-ai/)
  • CALM Hub (calm-hub/)
  • CALM Hub UI (calm-hub-ui/)
  • CALM Server (calm-server/)
  • CALM Widgets (calm-widgets/)
  • Documentation (docs/)
  • Shared (shared/)
  • VS Code Extension (calm-plugins/vscode/)
  • Dependencies
  • CI/CD

Commit Message Format ✅

Testing

  • I have tested my changes locally
  • I have added/updated unit tests
  • All existing tests pass

Verifying the benefit

Measured actual component re-renders per hover event with a temporary instrumented counter (not part of this diff), comparing main against this branch on the same diagram (finos.fluxnova / fluxnova-microservices: 11 regular nodes + 1 group node):

Branch Component re-renders on a single hover
main (before) all 11 CustomNodes + the group node
this branch (after) 0

Checking for regressions

  • Full unit suite: 1562 tests / 130 files pass, no regressions.
  • Lint: 0 errors (pre-existing, unrelated warnings only).
  • Manual verification against a real seeded backend, on a diagram with a group node and several children:
    • hover shows/elevates/clears correctly on both regular and grouped nodes, and the hover panel escapes the group boundary without clipping
    • dragging, container reflow, and layout persistence are unaffected
    • click-through to the node detail panel and control-jump links still work (confirms data callbacks survive memoization)
    • rapid hovering across adjacent nodes leaves no node stuck at an elevated z-index

Two pre-existing, unrelated issues turned up during manual testing and are being filed as separate tickets rather than fixed here, to keep this PR scoped to the performance change:

  • The hover popover can be clipped by the diagram pane's overflow boundary near the bottom "Controls" panel — pure CSS containment, reproducible on main too.
  • On mobile/touch, the hover popover and the click-triggered detail panel race each other, since the popover only closes via an incidental mouseleave rather than closing deterministically on click. The race itself exists on main too; this change only alters its visible symptom, because faster re-rendering changes the timing (main visibly flashes on every tap; this branch only on the first).

Checklist

  • My commits follow the conventional commit format
  • I have updated documentation if necessary
  • I have added tests for my changes (if applicable)
  • My changes follow the project's coding standards

…ver re-allocations

Wraps CustomNode, SystemGroupNode, and FloatingEdge in React.memo, and
changes the node hover handlers to only allocate a new node object
when its zIndex actually changes, instead of remapping every node on
every mouseenter/mouseleave. No behavior change, fewer re-renders on
large diagrams.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@aamanrebello
aamanrebello requested a review from a team as a code owner September 20, 2026 11:11
@github-actions github-actions Bot added the calm-hub-ui Affects `calm-hub-ui` label Sep 20, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

calm-hub-ui Affects `calm-hub-ui`

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant