@samitouri / QOS-React-2 / commits / d1dd7feabc

[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" />

Sebastian Markbåge committed Dec 14, 2024 at 13:46 UTC d1dd7feabc63bf8ca61e9b3f4d78245a29ebbe9a
2 files changed +7 -9
packages/react-reconciler/src/ReactFiberBeginWork.js
+5 -7
@@ -111,6 +111,7 @@ import {
111 disableLegacyMode,
112 disableDefaultPropsExceptForClasses,
113 enableOwnerStacks,
114 + enableHydrationLaneScheduling,
115 } from 'shared/ReactFeatureFlags';
116 import isArray from 'shared/isArray';
117 import shallowEqual from 'shared/shallowEqual';
@@ -146,6 +147,7 @@ import {
147 NoLane,
148 NoLanes,
149 OffscreenLane,
150 + DefaultLane,
151 DefaultHydrationLane,
152 SomeRetryLane,
153 includesSomeLane,
@@ -2646,14 +2648,10 @@ function mountDehydratedSuspenseComponent(
2648 // have timed out. In theory we could render it in this pass but it would have the
2649 // wrong priority associated with it and will prevent hydration of parent path.
2650 // Instead, we'll leave work left on it to render it in a separate commit.
2649 -
2650 - // TODO This time should be the time at which the server rendered response that is
2651 - // a parent to this boundary was displayed. However, since we currently don't have
2652 - // a protocol to transfer that time, we'll just estimate it by using the current
2653 - // time. This will mean that Suspense timeouts are slightly shifted to later than
2654 - // they should be.
2651 // Schedule a normal pri update to render this content.
2656 - workInProgress.lanes = laneToLanes(DefaultHydrationLane);
2652 + workInProgress.lanes = laneToLanes(
2653 + enableHydrationLaneScheduling ? DefaultLane : DefaultHydrationLane,
2654 + );
2655 } else {
2656 // We'll continue hydrating the rest at offscreen priority since we'll already
2657 // be showing the right content coming from the server, it is no rush.
packages/shared/ReactFeatureFlags.js
+2 -2
@@ -22,6 +22,8 @@
22 // when it rolls out to prod. We should remove these as soon as possible.
23 // -----------------------------------------------------------------------------
24
25 +export const enableHydrationLaneScheduling = true;
26 +
27 // -----------------------------------------------------------------------------
28 // Land or remove (moderate effort)
29 //
@@ -111,8 +113,6 @@ export const enableSuspenseAvoidThisFallback = false;
113
114 export const enableCPUSuspense = __EXPERIMENTAL__;
115
114 -export const enableHydrationLaneScheduling = true;
115 -
116 // Test this at Meta before enabling.
117 export const enableNoCloningMemoCache = false;
118