Conversation
…olve at once useCanAccess owns one query per record, and each authProvider.canAccess() call resolves on its own, so React commits each consumer separately. React 19 counts those commits as nested updates and throws past 50 of them, e.g. on a list with a link per row. Write the result of the checks that settle in the same tick inside a single notifyManager transaction, as useGetManyAggregate does since marmelab#11329. Fixes marmelab#11392
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
A canceled request can write stale authorization data if its query key is refetched before the old provider promise settles.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Batches concurrent useCanAccess results to prevent excessive React 19 commits.
Changes:
- Batches successful authorization results through React Query.
- Avoids restoring canceled queries.
- Adds batching and cancellation tests.
| File | Description |
|---|---|
packages/ra-core/src/auth/useCanAccess.ts |
Implements batched access-check resolution. |
packages/ra-core/src/auth/useCanAccess.spec.tsx |
Tests commit batching and canceled-query handling. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The batch looked a query up by key, so a check canceled and then refetched before its authProvider call settled wrote its stale result into the new fetch. Each check now carries the AbortSignal of its own fetch, which react-query aborts on cancel and removal.
fzaninotto
requested changes
Oct 2, 2026
A setTimeout(0) delayed every check by a task. react-query already delivers the notifications on its own setTimeout(0), so a microtask is enough to share one notifyManager batch. The cancel-then-refetch test now awaits the refetch.
This branch has not been deployed
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.

Problem
Fixes #11392.
On React 19, the first load of a list page with many rows throws
Maximum update depth exceededwhen theauthProviderimplementscanAccess:useCanAccesskeys its query onrecordId, so every row owns a distinct query (therowClicklink, the link of each<ReferenceField>, and so on), and everyauthProvider.canAccess()call resolves on its own. react-query then flushes one observer notification per query, each in its ownsetTimeout(0)task, and React commits each of them separately. React 19 counts a commit as nested when it leaves a Sync or Default lane pending, where React 18 counted only a pending Sync lane, so these commits pass the limit of 50.Measured on an app with
perPage={100}: about 250canAccessqueries on one list page, and one to three uncaught errors per first load, with or without<StrictMode>. The page recovers, but every error reaches error monitoring.Solution
The same approach as #11329 for
useGetManyAggregate. The checks that settle in the same tick are collected, their result is written withqueryClient.setQueryDatainside a singlenotifyManager.batch, and then their promises resolve to let their query leave the fetching state. All the consumers get their result in one commit.Checks whose query was canceled or removed while in flight are skipped, so their cache entry is not resurrected. Errors keep their current path. There is no public API change. A result reaches its consumers one
setTimeout(0)later than before.How To Test
Two tests added to
useCanAccess.spec.tsx:Expected: 1, Received: 30.Received: true.The overflow itself needs React 19, and this repository tests on React 18. It was verified in an app on React 19.3.0 with this change applied to ra-core 5.15.4 as a patch: two list pages of 100 rows went from one to three uncaught errors per first load to none, and every
canAccessquery ended instatus: 'success',fetchStatus: 'idle'.The 21 spec files that exercise
canAccesspass, as do the fullra-coreandra-ui-materialuisuites.Additional Checks
masterfor a bugfix or a documentation fix, ornextfor a featureAlso, please make sure to read the contributing guidelines.