Skip to content

Preserve imperative tree.update() props across node toggles#337

Merged
TrevorBurnham merged 1 commit into
jameskerr:mainfrom
TrevorBurnham:fix/tree-update-preserves-imperative-props
May 11, 2026
Merged

Preserve imperative tree.update() props across node toggles#337
TrevorBurnham merged 1 commit into
jameskerr:mainfrom
TrevorBurnham:fix/tree-update-preserves-imperative-props

Conversation

@TrevorBurnham
Copy link
Copy Markdown
Collaborator

Picks up #229 from @iamyunsin (which targeted the old packages/ path and went stale), ports the fix to the current modules/ layout, and credits the original author via Co-Authored-By.

The bug

Closes #228 (partially — see Notes).

TreeProvider had a single useMemo that re-ran on either treeProps changes or state.nodes.open changes, always calling api.update(treeProps). That meant any prop a consumer set imperatively via tree.update({ ... }) would be reverted to the parent's React props the next time a node was toggled.

The fix

Split the effect:

  • One useMemo syncs from treeProps (same as before, minus the open-state dep).
  • A second useMemo rebuilds visible nodes on state.nodes.open changes using api.props — preserving any imperative changes.

Backward compatible: when no one calls api.update() imperatively, api.props === treeProps, so behavior is unchanged.

Notes

  • Partial fix for There is a bug in using the tree.update method to set height. #228. The reporter also noted that tree.update() ''does not take effect immediately.'' That's a separate issue — api.update() mutates this.props but doesn't dispatch a Redux action, so useSyncExternalStore doesn't notify. That requires a different fix and isn't in scope here.
  • No regression test. The repo currently has only a node-environment Jest setup with one TreeApi unit test. Reproducing this bug requires React component rendering (jsdom + @testing-library/react), which is a larger infra change. Happy to do that in a follow-up if you'd like.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings May 10, 2026 21:33
Copy link
Copy Markdown

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

Ports and updates the fix for #228/#229 to the current modules/ layout by separating “sync TreeApi props from React props” from “rebuild visible nodes on open/close changes”, so imperative tree.update() calls don’t get overwritten when nodes are toggled.

Changes:

  • Removed state.nodes.open from the “sync from treeProps” update dependencies.
  • Added a second memo keyed on state.nodes.open that rebuilds visible nodes using api.props to preserve imperative updates.

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


/* Rebuild visible nodes when open state changes, without clobbering
props set imperatively via api.update(). */
useMemo(() => {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch — you nailed it. CI surfaced the same regression and the amended commit (33e6de2) now bumps updateCount.current in the open-state memo as you suggested. Verified locally with the gmail-spec e2e (12/12 passing).

When a consumer calls tree.update() via the imperative handle, those
props live on api.props. The existing useMemo re-ran on state.nodes.open
changes and called api.update(treeProps), reverting api.props to the
parent's React props and losing any imperative changes.

Split into two effects: one syncs from treeProps, the other rebuilds
visible nodes on open-state changes using api.props.

Co-Authored-By: iamyunsin <yunsin@vip.qq.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@TrevorBurnham TrevorBurnham force-pushed the fix/tree-update-preserves-imperative-props branch from 6255487 to 33e6de2 Compare May 11, 2026 01:42
TrevorBurnham added a commit to TrevorBurnham/react-arborist that referenced this pull request May 11, 2026
Adds jest-environment-jsdom, @testing-library/react, and a React
dev dependency for testing. Switches the jest testEnvironment to
jsdom and adds a regression test that renders <Tree>, calls
api.update() via the imperative ref to change rowHeight, then
opens a node — verifying the imperative prop survives the
resulting state.nodes.open change.

The test fails on main (without the jameskerr#337 fix) and passes with it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@TrevorBurnham
Copy link
Copy Markdown
Collaborator Author

Force-pushed an amended commit that fixes a regression caught by CI.

What was wrong: the original split-useMemo fix dropped updateCount.current += 1 from the open-state path. DefaultContainer (and useDataUpdates consumers generally) only re-render when DataUpdatesContext changes — so without the bump, state.nodes.open changes no longer triggered a re-render of the FixedSizeList, and itemCount stayed stale. That broke the Gmail demo's collapse/expand behavior (6/12 e2e tests).

Fix: bump updateCount.current inside the second useMemo too.

Verified locally: yarn cypress run --spec cypress/e2e/gmail-spec.cy.js → 12/12 passing with the corrected commit, vs. 6/12 failing before.

@TrevorBurnham TrevorBurnham merged commit 2e09b70 into jameskerr:main May 11, 2026
1 check passed
TrevorBurnham added a commit to TrevorBurnham/react-arborist that referenced this pull request May 11, 2026
Adds jest-environment-jsdom, @testing-library/react, and a React
dev dependency for testing. Switches the jest testEnvironment to
jsdom and adds a regression test that renders <Tree>, calls
api.update() via the imperative ref to change rowHeight, then
opens a node — verifying the imperative prop survives the
resulting state.nodes.open change.

The test fails on main (without the jameskerr#337 fix) and passes with it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@TrevorBurnham TrevorBurnham deleted the fix/tree-update-preserves-imperative-props branch May 11, 2026 02:22
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.

There is a bug in using the tree.update method to set height.

2 participants