Skip to content

fix(core): prevent unnecessary node re-renders during canvas zoom#2398

Open
cyphercodes wants to merge 1 commit intodidi:masterfrom
cyphercodes:fix-zoom-rerender
Open

fix(core): prevent unnecessary node re-renders during canvas zoom#2398
cyphercodes wants to merge 1 commit intodidi:masterfrom
cyphercodes:fix-zoom-rerender

Conversation

@cyphercodes
Copy link
Copy Markdown

Description

Fixes #2396

When zooming the canvas, all nodes were unnecessarily re-rendering due to SCALE_X being destructured from the observable transformModel in the render() method. This caused mobx to track SCALE_X as a reactive dependency, triggering re-renders for every node whenever the zoom level changed.

Changes

  • Changed from destructuring SCALE_X in render() to accessing it directly via transformModel.SCALE_X when needed
  • This prevents mobx from establishing a reactive dependency on SCALE_X at the component level
  • The drag step is still correctly updated with the current scale, but without causing full re-renders

Impact

  • Significantly improved performance when zooming on diagrams with many nodes
  • Smoother zoom interactions, especially on complex flowcharts
  • No functional changes to the drag behavior

Testing

  • Verified the fix by checking that setStep() is still called with the correct scaled grid size
  • The drag behavior continues to work correctly at different zoom levels

Remove SCALE_X from destructuring in BaseNode render() to prevent
mobx from tracking it as a reactive dependency. This fixes the
performance issue where all nodes re-render when zooming the canvas.

Closes didi#2396
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 5, 2026

⚠️ No Changeset found

Latest commit: 3df4338

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@ZivvW
Copy link
Copy Markdown
Contributor

ZivvW commented Apr 7, 2026

Thank you for submitting this pull request and proposing a solution to the issue.

However, after reviewing the change, I believe it only addresses the re-render problem when the conditional statement is not triggered. When the node is draggable (i.e., when the condition is satisfied), a re-render still occurs during canvas zoom operations.

I believe a better approach would be to move this.stepDrag.setStep out of the render function and execute it only in handleMouseDown, to keep it consistent with the other this.stepDrag.setStep calls elsewhere in the codebase:

this.stepDrag.setStep(gridSize * SCALE_X)
this.stepDrag.handleMouseDown(ev)

this.stepDrag.setStep(gridSize * SCALE_X)
this.stepDrag.handleMouseDown(ev)

Please note that this alternative solution has not been thoroughly tested yet. Additionally, I do not use snapGrid in my project.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Optimizes node view rendering during canvas zoom by attempting to prevent MobX from tracking transformModel.SCALE_X as a reactive dependency in BaseNode.render(), addressing performance regression reported in #2396.

Changes:

  • Stop destructuring SCALE_X from graphModel.transformModel in BaseNode.render().
  • Update stepDrag.setStep() to use transformModel.SCALE_X instead of a destructured SCALE_X variable.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 558 to 560
if (adjustNodePosition && draggable) {
this.stepDrag.setStep(gridSize * SCALE_X)
this.stepDrag.setStep(gridSize * transformModel.SCALE_X)
}
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still reads the observable transformModel.SCALE_X during render(), so an observer-wrapped node view will continue to track SCALE_X and re-render on every zoom change. Switching from destructuring to transformModel.SCALE_X doesn’t avoid MobX dependency tracking; it only changes when the read occurs.

To actually prevent zoom-triggered node re-renders, avoid reading SCALE_X in render() entirely (e.g., move setStep() into handleMouseDown right before stepDrag.handleMouseDown, or wrap the read in mobx.untracked).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug Report]: 画布缩放时所有节点 re-render,导致卡顿

3 participants