Skip to content

Commit b04254f

Browse files
authored
Don't try to hydrate a hidden Offscreen tree (#32862)
I found a bug even before the Activity hydration stuff. If we're hydrating an Offscreen boundary in its "hidden" state it won't have any content to hydrate so will trigger hydration errors (which are then eaten by the Offscreen boundary itself). Leaving it not prewarmed. This doesn't happen in the simple case because we'd be hydrating at a higher priority than Offscreen at the root, and those are deferred to Offscreen by not having higher priority. However, we've hydrating at the Offscreen priority, which we do inside Suspense boundaries, then it tries to hydrate against an empty set. I ended up moving this to the Activity boundary in a future PR since it's the SSR side that decided where to not render something and it only has a concept of Activity, no Offscreen. 1dc05a5#diff-d5166797ebbc5b646a49e6a06a049330ca617985d7a6edf3ad1641b43fde1ddfR1111
1 parent 539bbdb commit b04254f

File tree

2 files changed

+49
-1
lines changed

2 files changed

+49
-1
lines changed

packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3723,6 +3723,11 @@ describe('ReactDOMServerPartialHydration', () => {
37233723
<Activity mode="hidden">
37243724
<HiddenChild />
37253725
</Activity>
3726+
<Suspense fallback={null}>
3727+
<Activity mode="hidden">
3728+
<HiddenChild />
3729+
</Activity>
3730+
</Suspense>
37263731
</>
37273732
);
37283733
}
@@ -3743,6 +3748,10 @@ describe('ReactDOMServerPartialHydration', () => {
37433748
</span>
37443749
<!--&-->
37453750
<!--/&-->
3751+
<!--$-->
3752+
<!--&-->
3753+
<!--/&-->
3754+
<!--/$-->
37463755
</div>
37473756
`);
37483757

@@ -3758,6 +3767,7 @@ describe('ReactDOMServerPartialHydration', () => {
37583767
await waitForPaint([]);
37593768
}
37603769
// Subsequently, the hidden child is prerendered on the client
3770+
// along with hydrating the Suspense boundary outside the Activity.
37613771
await waitForPaint(['HiddenChild']);
37623772
expect(container).toMatchInlineSnapshot(`
37633773
<div>
@@ -3766,6 +3776,37 @@ describe('ReactDOMServerPartialHydration', () => {
37663776
</span>
37673777
<!--&-->
37683778
<!--/&-->
3779+
<!--$-->
3780+
<!--&-->
3781+
<!--/&-->
3782+
<!--/$-->
3783+
<span
3784+
style="display: none;"
3785+
>
3786+
Hidden
3787+
</span>
3788+
</div>
3789+
`);
3790+
3791+
// Next the child inside the Activity is hydrated.
3792+
await waitForPaint(['HiddenChild']);
3793+
3794+
expect(container).toMatchInlineSnapshot(`
3795+
<div>
3796+
<span>
3797+
Visible
3798+
</span>
3799+
<!--&-->
3800+
<!--/&-->
3801+
<!--$-->
3802+
<!--&-->
3803+
<!--/&-->
3804+
<!--/$-->
3805+
<span
3806+
style="display: none;"
3807+
>
3808+
Hidden
3809+
</span>
37693810
<span
37703811
style="display: none;"
37713812
>

packages/react-reconciler/src/ReactFiberBeginWork.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,14 @@ function updateOffscreenComponent(
712712
}
713713
reuseHiddenContextOnStack(workInProgress);
714714
pushOffscreenSuspenseHandler(workInProgress);
715-
} else if (!includesSomeLane(renderLanes, (OffscreenLane: Lane))) {
715+
} else if (
716+
!includesSomeLane(renderLanes, (OffscreenLane: Lane)) ||
717+
// SSR doesn't render hidden content (except legacy hidden) so it shouldn't hydrate,
718+
// even at offscreen lane. Defer to a client rendered offscreen lane.
719+
(getIsHydrating() &&
720+
(!enableLegacyHidden ||
721+
nextProps.mode !== 'unstable-defer-without-hiding'))
722+
) {
716723
// We're hidden, and we're not rendering at Offscreen. We will bail out
717724
// and resume this tree later.
718725

0 commit comments

Comments
 (0)