[Fiber] Schedule client renders using non-hydration lane (#31776)

Related to #31752.

When hydrating, we have two different ways of handling a Suspense
boundary that the server has already given up on and decided to client
render. If we have already hydrated the parent and then later this
happens, then we'll use the retry lane like any ping. If we discover
that it was already in client-render mode when we discover the Suspense
boundary for the first time, then schedule a default lane to let us
first finish the current render and then upgrade the priority to sync to
try to client render this boundary as soon as possible since we're
holding back content.

We used to use the `DefaultHydrationLane` for this but this is not
really a Hydration. It's actually a client render. If we get any other
updates flowing in from above at the same time we might as well do them
in the same pass instead of two passes. So this should be considered
more like any update.

This also means that visually the client render pass now gets painted as
a render instead of a hydration.

This show the flow of a shell being hydrated at the default priority,
then a Suspense boundary being discovered and hydrated at Idle and then
an inner boundary being discovered as client rendered which gets
upgraded to default.

<img width="1363" alt="Screenshot 2024-12-14 at 12 13 57 AM"
src="https://github.com/user-attachments/assets/a141133e-4856-4f38-a11f-f26bd00b6245"
/>
This commit is contained in:
Sebastian Markbåge
2024-12-14 13:46:21 -05:00
committed by GitHub
parent 9e2c233139
commit d1dd7feabc
2 changed files with 7 additions and 9 deletions
+5 -7
View File
@@ -111,6 +111,7 @@ import {
disableLegacyMode,
disableDefaultPropsExceptForClasses,
enableOwnerStacks,
enableHydrationLaneScheduling,
} from 'shared/ReactFeatureFlags';
import isArray from 'shared/isArray';
import shallowEqual from 'shared/shallowEqual';
@@ -146,6 +147,7 @@ import {
NoLane,
NoLanes,
OffscreenLane,
DefaultLane,
DefaultHydrationLane,
SomeRetryLane,
includesSomeLane,
@@ -2646,14 +2648,10 @@ function mountDehydratedSuspenseComponent(
// have timed out. In theory we could render it in this pass but it would have the
// wrong priority associated with it and will prevent hydration of parent path.
// Instead, we'll leave work left on it to render it in a separate commit.
// TODO This time should be the time at which the server rendered response that is
// a parent to this boundary was displayed. However, since we currently don't have
// a protocol to transfer that time, we'll just estimate it by using the current
// time. This will mean that Suspense timeouts are slightly shifted to later than
// they should be.
// Schedule a normal pri update to render this content.
workInProgress.lanes = laneToLanes(DefaultHydrationLane);
workInProgress.lanes = laneToLanes(
enableHydrationLaneScheduling ? DefaultLane : DefaultHydrationLane,
);
} else {
// We'll continue hydrating the rest at offscreen priority since we'll already
// be showing the right content coming from the server, it is no rush.
+2 -2
View File
@@ -22,6 +22,8 @@
// when it rolls out to prod. We should remove these as soon as possible.
// -----------------------------------------------------------------------------
export const enableHydrationLaneScheduling = true;
// -----------------------------------------------------------------------------
// Land or remove (moderate effort)
//
@@ -111,8 +113,6 @@ export const enableSuspenseAvoidThisFallback = false;
export const enableCPUSuspense = __EXPERIMENTAL__;
export const enableHydrationLaneScheduling = true;
// Test this at Meta before enabling.
export const enableNoCloningMemoCache = false;