Skip to content

Conversation

stipsan
Copy link
Member

@stipsan stipsan commented Sep 18, 2025

Description

v3.0.11 introduced a regression affecting Sanity Studios running on React 18. It did not impact users on React 19.
The issue was inputs using <Autocomplete> often crashing with this error:

react-dom.development.js:27331 Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
    at checkForNestedUpdates (react-dom.development.js:27331:11)
    at scheduleUpdateOnFiber (react-dom.development.js:25514:3)
    at dispatchSetState (react-dom.development.js:16708:7)
    at refObject (react-dom.development.js:16316:7)
    at safelyCallDestroy (react-dom.development.js:22971:5)
    at commitHookEffectListUnmount (react-dom.development.js:23139:11)
    at commitMutationEffectsOnFiber (react-dom.development.js:24351:15)
    at recursivelyTraverseMutationEffects (react-dom.development.js:24312:7)
    at commitMutationEffectsOnFiber (react-dom.development.js:24385:9)
    at recursivelyTraverseMutationEffects (react-dom.development.js:24312:7)

I made a throwaway deploy of our test studio on react 18, where this can be reproduced by interacting with the reference input:

Screen.Recording.2025-09-18.at.19.46.23.mov

I published a canary of the fix on this branch (@sanity/[email protected]) and updated the throwaway deployment:

Screen.Recording.2025-09-18.at.19.47.48.mov

After going down the rabbit hole it turns out the crash is triggered by the setInputElement state call in <Autocomplete>, though only on React 18 and not React 19.
Given that 19 batches all state updates, while 18 only batches state updates it started making sense.
With the batching in 19 state update is scheduled and thus the recursion limit isn't hit, while in 18 it's sync and now it crashes.

By wrapping it in startTransition it opts-in to batching in 18, while behaving mostly the same in 19. The difference being that react might choose to delay rendering with the referenceElement set if other state updates happen that respond to user input and such. In this case that's fine, if the user is typing in a text input it's more important to update the text input, than render the popover right away.

Alternatively we could use import {unstable_batchedUpdates} from 'react-dom', but I decided against it since it's not only prefixed unstable, it's also undocumented.

What to review

Is there enough context in the code comments?
I've applied the same fix to <TreeItem>, though we don't use it in the Studio codebase apps that use it run a similar risk on React 18 as <Autocomplete>.

Testing

Existing test suite is enough to cover 19. I've manually tested 18, and there's a test deployment you can use to confirm:

@vercel
Copy link

vercel bot commented Sep 18, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
sanity-ui-storybook Ready Ready Preview Comment Sep 18, 2025 5:12pm
sanity-ui-workshop Ready Ready Preview Comment Sep 18, 2025 5:12pm

@stipsan stipsan marked this pull request as ready for review September 18, 2025 17:48
@stipsan stipsan enabled auto-merge (squash) September 18, 2025 17:48
@stipsan stipsan disabled auto-merge September 18, 2025 17:53
@stipsan stipsan merged commit 5aa8489 into main Sep 18, 2025
25 checks passed
@stipsan stipsan deleted the fix-react-18-regression branch September 18, 2025 17:53
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.

1 participant