Fix useId hydration error in strict mode #3980
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.
Closes #3637, closes #3637
This fixes a hydration error when using
useIdwith ReactStrictMode. Due to component render functions being called multiple times on the client but not on the server, the ids would mismatch. Several approaches to fixing this have been proposed (thanks to @ciffelia and others). One in #3637 is to switch toReact.useIdon React 18, which we still might do, but we need to fix 16 and 17 as well. Another in #3963 is to require a newstrictModeprop to explicitly opt-into double incrementing on the server. That will be harder to do correctly, especially in libraries where StrictMode is unknown.This PR implements an alternate approach using some React internals to get a stable reference to the Fiber node for the component. With this we can detect the second render and reset our counter back to what it was the first time, which in effect means it is only incremented once. This ensures the id never changes after the first render and therefore matches the server. See the code comment for a more detail explanation.
As mentioned before, we may still want to switch to
React.useIdfor React 18 to handle other edge cases I'm not aware of with the new streaming renderer, though this should continue to work as before if you put an SSRProvider around every Suspense boundary. Switching that would change the format of the returned ids though, so might break people's unit tests (especially snapshots), so I am hesitant. Thoughts appreciated.