Fix ComboBox re-opening after selection #5354
Merged
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.
Fixes #5120
This fixes ComboBox re-opening after selecting an option in the RAC docs. The problem stems from how React batches updates, and how our collection implementation works. Our collection implementation uses React's
useSyncExternalStorehook to trigger updates. Inside ComboBox, we have alastValueref, and aninputValuestate, which are updated simultaneously. Then, in the next render, we assume thatlastValue.currentwill equalinputValue. However, this assumption is not true when some ancestor component usesuseSyncExternalStore, which triggers a synchronous update. Essentially,setStateis asynchronous, but a synchronous update can preempt it. In that case, the ref will already be updated but the state will not. This CodeSandbox has a minimal reproduction.The solution is to move
lastValueto state instead of a ref. There are probably a lot more instances of this pattern that we should audit throughout the codebase, but this is the first real world case where this assumption turned out to be incorrect.This started appearing in our docs due to the change from
ReactDOM.rendertoReactDOM.createRoot.renderbehaves like React 17, so state updates run synchronously.createRootopts-into concurrent features.