Skip to content

Commit

Permalink
Move tail hydration mismatch back to hydration context (#28501)
Browse files Browse the repository at this point in the history
In #23176 we added a special case in completeWork for SuspenseBoundaries
if they still have trailing children. However, that misses a case
because it doesn't log a recoverable error for the hydration mismatch.
So we get an error that we rerendered.

I think this special case was done to avoid contexts getting out of
sync. I don't know why we didn't just move where the pop happens though
so that's what I did here and let the regular pass throw instead. Seems
to be pass the tests.
  • Loading branch information
sebmarkbage committed Mar 6, 2024
1 parent 966d174 commit c11b196
Show file tree
Hide file tree
Showing 3 changed files with 5 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ describe('ReactDOMServerPartialHydration', () => {
assertLog([
'Server rendered',
'Client rendered',
'Hydration failed because the initial UI does not match what was rendered on the server.',
'There was an error while hydrating this Suspense boundary. ' +
'Switched to client rendering.',
]);
Expand Down
19 changes: 4 additions & 15 deletions packages/react-reconciler/src/ReactFiberCompleteWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,9 @@ import {
prepareToHydrateHostInstance,
prepareToHydrateHostTextInstance,
prepareToHydrateHostSuspenseInstance,
warnIfUnhydratedTailNodes,
popHydrationState,
resetHydrationState,
getIsHydrating,
hasUnhydratedTailNodes,
upgradeHydrationErrorsToRecoverable,
} from './ReactFiberHydrationContext';
import {
Expand Down Expand Up @@ -866,18 +864,6 @@ function completeDehydratedSuspenseBoundary(
workInProgress: Fiber,
nextState: SuspenseState | null,
): boolean {
if (
hasUnhydratedTailNodes() &&
(workInProgress.mode & ConcurrentMode) !== NoMode &&
(workInProgress.flags & DidCapture) === NoFlags
) {
warnIfUnhydratedTailNodes(workInProgress);
resetHydrationState();
workInProgress.flags |= ForceClientRender | DidCapture;

return false;
}

const wasHydrated = popHydrationState(workInProgress);

if (nextState !== null && nextState.dehydrated !== null) {
Expand Down Expand Up @@ -1337,7 +1323,6 @@ function completeWork(
return null;
}
case SuspenseComponent: {
popSuspenseHandler(workInProgress);
const nextState: null | SuspenseState = workInProgress.memoizedState;

// Special path for dehydrated boundaries. We may eventually move this
Expand All @@ -1358,10 +1343,12 @@ function completeWork(
);
if (!fallthroughToNormalSuspensePath) {
if (workInProgress.flags & ForceClientRender) {
popSuspenseHandler(workInProgress);
// Special case. There were remaining unhydrated nodes. We treat
// this as a mismatch. Revert to client rendering.
return workInProgress;
} else {
popSuspenseHandler(workInProgress);
// Did not finish hydrating, either because this is the initial
// render or because something suspended.
return null;
Expand All @@ -1371,6 +1358,8 @@ function completeWork(
// Continue with the normal Suspense path.
}

popSuspenseHandler(workInProgress);

if ((workInProgress.flags & DidCapture) !== NoFlags) {
// Something suspended. Re-render with the fallback children.
workInProgress.lanes = renderLanes;
Expand Down
6 changes: 0 additions & 6 deletions packages/react-reconciler/src/ReactFiberHydrationContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -893,10 +893,6 @@ function popHydrationState(fiber: Fiber): boolean {
return true;
}

function hasUnhydratedTailNodes(): boolean {
return isHydrating && nextHydratableInstance !== null;
}

function warnIfUnhydratedTailNodes(fiber: Fiber) {
let nextInstance = nextHydratableInstance;
while (nextInstance) {
Expand Down Expand Up @@ -952,6 +948,4 @@ export {
prepareToHydrateHostTextInstance,
prepareToHydrateHostSuspenseInstance,
popHydrationState,
hasUnhydratedTailNodes,
warnIfUnhydratedTailNodes,
};

0 comments on commit c11b196

Please sign in to comment.