Skip to content

[tree view] Discard superseded lazy-loading responses#23005

Open
Anexus5919 wants to merge 1 commit into
mui:masterfrom
Anexus5919:fix/tree-lazy-loading-stale-response
Open

[tree view] Discard superseded lazy-loading responses#23005
Anexus5919 wants to merge 1 commit into
mui:masterfrom
Anexus5919:fix/tree-lazy-loading-stale-response

Conversation

@Anexus5919

Copy link
Copy Markdown
Contributor

Fixes a race in the Tree View Pro lazy-loading plugin where a slower, older fetch could overwrite a newer response for the same item.

fetchItemChildren applied the awaited getTreeItems() response to the cache and the store unconditionally, with no request-ordering guard. Because updateItemChildren (a forced refresh) bypasses the NestedDataManager queue and calls fetchItemChildren directly, it can run concurrently with an in-flight expand fetch for the same item. Whichever promise resolved last won, so an older expand response could clobber a newer refresh in both the store and the cache, and the stale data persisted until the cache TTL expired.

The fix adds a per-item monotonic request id, captured before each fetch. The response (and any error) is applied only when it is still the latest request for that item. It is per-item, rather than a single global counter like the Data Grid data source, because the tree fetches many different items concurrently, so a global counter would wrongly discard valid sibling fetches.

Closes #22959

Changelog

Tree View Pro lazy loading no longer applies a stale getTreeItems() response when a forced refresh races an in-flight expand fetch for the same item.

@Anexus5919

Copy link
Copy Markdown
Contributor Author

@rita-codes @michelengelen Kindly have a review on this pr. Thanks!

@code-infra-dashboard

code-infra-dashboard Bot commented Jun 29, 2026

Copy link
Copy Markdown

Deploy preview

https://deploy-preview-23005--material-ui-x.netlify.app/
QR code for https://deploy-preview-23005--material-ui-x.netlify.app/

Bundle size

Bundle Parsed size Gzip size
@mui/x-data-grid 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-pro 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-premium 0B(0.00%) 0B(0.00%)
@mui/x-charts 0B(0.00%) 0B(0.00%)
@mui/x-charts-pro 0B(0.00%) 0B(0.00%)
@mui/x-charts-premium 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers-pro 0B(0.00%) 0B(0.00%)
@mui/x-tree-view 0B(0.00%) 0B(0.00%)
@mui/x-tree-view-pro 🔺+320B(+0.20%) 🔺+72B(+0.14%)
@mui/x-scheduler 0B(0.00%) 0B(0.00%)
@mui/x-scheduler-premium 0B(0.00%) 0B(0.00%)
@mui/x-license 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bea6ad3f25

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +303 to +305
if (this.lastRequestId.get(cacheKey) !== requestId) {
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep superseded expand pending until the latest fetch finishes

When the stale expand request resolves before the newer updateItemChildren request, this return makes fetchItems() complete even though the fresh request is still pending; the finally below also clears the loading state. In that ordering, handleBeforeItemToggleExpansion proceeds to expand the item and re-run selection propagation before the fresh children have been inserted, so a selected parent with selectionPropagation.descendants will not select the children loaded by the refresh. The stale response/error should be skipped without settling the user-visible expand/loading flow ahead of the current request.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

@Anexus5919 Anexus5919 force-pushed the fix/tree-lazy-loading-stale-response branch from bea6ad3 to 4b58291 Compare June 29, 2026 16:58
@Anexus5919

Copy link
Copy Markdown
Contributor Author

Good catch, thanks for the careful read.

You're right: in the ordering where the expand fetch resolves before the racing updateItemChildren refresh, the superseded expand request bailing out via return would resolve fetchItems() and clear the loading state, so handleBeforeItemToggleExpansion would expand the item and re-run selectionPropagation.descendants before the newer response's children were inserted — leaving the freshly-loaded children unselected.

I've reworked the guard to fix this. Instead of only letting the latest-started request apply (and discarding everyone else), it now applies a response unless a newer response has already been applied, by tracking the highest applied request id per item:

  • the older expand request, when it resolves first, applies its own children, so the expand/propagation flow sees them;
  • a stale response that resolves after a newer one was already applied is still discarded (the original bug);
  • an out-of-order older error no longer sticks once a newer success applies.

Added a regression test covering the expand-resolves-first ordering with selectionPropagation: { descendants: true }, asserting the child loaded for the selected parent ends up selected.

@zannager zannager added the scope: tree view Changes related to the tree view. This includes TreeView, TreeItem. label Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: tree view Changes related to the tree view. This includes TreeView, TreeItem.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[tree view] Lazy loading applies a stale getTreeItems() response when a refresh races an expand fetch

2 participants