Skip to content

Commit 857a68d

Browse files
committed
useDeferredValue has higher priority than partial hydration (#27550)
By default, partial hydration is given the lowest possible priority, because until a tree is updated, the server-rendered HTML is assumed to match the final resolved HTML. However, this isn't completely true because a component may choose to "upgrade" itself upon hydration. The simplest example is a component that calls setState in a useEffect to switch to a richer implementation of the UI. Another example is a component that doesn't have a server- rendered implementation, so it intentionally suspends to force a client- only render. useDeferredValue is an example, too: the server only renders the first pass (the initialValue) argument, and relies on the client to upgrade to the final value. What we should really do in these cases is emit some information into the Fizz stream so that Fiber knows to prioritize the hydration of certain trees. We plan to add a mechanism for this in the future. In the meantime, though, we can at least ensure that the priority of the upgrade task is correct once it's "discovered" during hydration. In this case, the priority of the task spawned by useDeferredValue should have Transition priority, not Offscreen priority. DiffTrain build for [779d593](779d593)
1 parent dc239e6 commit 857a68d

23 files changed

+207
-119
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6db7f4209e6f32ebde298a0b7451710dd6aa3e19
1+
779d59374e8e852ce00728d895156a7574e1b456

compiled/facebook-www/React-dev.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ if (
2727
}
2828
"use strict";
2929

30-
var ReactVersion = "18.3.0-www-classic-63396f54";
30+
var ReactVersion = "18.3.0-www-classic-273dc50b";
3131

3232
// ATTENTION
3333
// When adding new symbols to this file,

compiled/facebook-www/React-prod.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,4 +587,4 @@ exports.useSyncExternalStore = function (
587587
exports.useTransition = function () {
588588
return ReactCurrentDispatcher.current.useTransition();
589589
};
590-
exports.version = "18.3.0-www-classic-a546ef8a";
590+
exports.version = "18.3.0-www-classic-252d6948";

compiled/facebook-www/React-prod.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,4 +579,4 @@ exports.useSyncExternalStore = function (
579579
exports.useTransition = function () {
580580
return ReactCurrentDispatcher.current.useTransition();
581581
};
582-
exports.version = "18.3.0-www-modern-ed902e38";
582+
exports.version = "18.3.0-www-modern-a27fae8a";

compiled/facebook-www/React-profiling.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ exports.useSyncExternalStore = function (
590590
exports.useTransition = function () {
591591
return ReactCurrentDispatcher.current.useTransition();
592592
};
593-
exports.version = "18.3.0-www-modern-82bf9b55";
593+
exports.version = "18.3.0-www-modern-53a85d2a";
594594

595595
/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
596596
if (

compiled/facebook-www/ReactART-dev.classic.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function _assertThisInitialized(self) {
6969
return self;
7070
}
7171

72-
var ReactVersion = "18.3.0-www-classic-bb3a0070";
72+
var ReactVersion = "18.3.0-www-classic-a2f705e2";
7373

7474
var LegacyRoot = 0;
7575
var ConcurrentRoot = 1;
@@ -2281,8 +2281,10 @@ function markSpawnedDeferredLane(root, spawnedLane, entangledLanes) {
22812281
root.entangledLanes |= spawnedLane;
22822282
root.entanglements[spawnedLaneIndex] |=
22832283
DeferredLane | // If the parent render task suspended, we must also entangle those lanes
2284-
// with the spawned task.
2285-
entangledLanes;
2284+
// with the spawned task, so that the deferred task includes all the same
2285+
// updates that the parent task did. We can exclude any lane that is not
2286+
// used for updates (e.g. Offscreen).
2287+
(entangledLanes & UpdateLanes);
22862288
}
22872289

22882290
function markRootEntangled(root, entangledLanes) {
@@ -24202,13 +24204,20 @@ function requestDeferredLane() {
2420224204
// If there are multiple useDeferredValue hooks in the same render, the
2420324205
// tasks that they spawn should all be batched together, so they should all
2420424206
// receive the same lane.
24205-
if (includesSomeLane(workInProgressRootRenderLanes, OffscreenLane)) {
24207+
// Check the priority of the current render to decide the priority of the
24208+
// deferred task.
24209+
// OffscreenLane is used for prerendering, but we also use OffscreenLane
24210+
// for incremental hydration. It's given the lowest priority because the
24211+
// initial HTML is the same as the final UI. But useDeferredValue during
24212+
// hydration is an exception — we need to upgrade the UI to the final
24213+
// value. So if we're currently hydrating, we treat it like a transition.
24214+
var isPrerendering =
24215+
includesSomeLane(workInProgressRootRenderLanes, OffscreenLane) &&
24216+
!getIsHydrating();
24217+
24218+
if (isPrerendering) {
2420624219
// There's only one OffscreenLane, so if it contains deferred work, we
2420724220
// should just reschedule using the same lane.
24208-
// TODO: We also use OffscreenLane for hydration, on the basis that the
24209-
// initial HTML is the same as the hydrated UI, but since the deferred
24210-
// task will change the UI, it should be treated like an update. Use
24211-
// TransitionHydrationLane to trigger selective hydration.
2421224221
workInProgressDeferredLane = OffscreenLane;
2421324222
} else {
2421424223
// Everything else is spawned as a transition.

compiled/facebook-www/ReactART-dev.modern.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function _assertThisInitialized(self) {
6969
return self;
7070
}
7171

72-
var ReactVersion = "18.3.0-www-modern-f6a06001";
72+
var ReactVersion = "18.3.0-www-modern-0b462672";
7373

7474
var LegacyRoot = 0;
7575
var ConcurrentRoot = 1;
@@ -2278,8 +2278,10 @@ function markSpawnedDeferredLane(root, spawnedLane, entangledLanes) {
22782278
root.entangledLanes |= spawnedLane;
22792279
root.entanglements[spawnedLaneIndex] |=
22802280
DeferredLane | // If the parent render task suspended, we must also entangle those lanes
2281-
// with the spawned task.
2282-
entangledLanes;
2281+
// with the spawned task, so that the deferred task includes all the same
2282+
// updates that the parent task did. We can exclude any lane that is not
2283+
// used for updates (e.g. Offscreen).
2284+
(entangledLanes & UpdateLanes);
22832285
}
22842286

22852287
function markRootEntangled(root, entangledLanes) {
@@ -23867,13 +23869,20 @@ function requestDeferredLane() {
2386723869
// If there are multiple useDeferredValue hooks in the same render, the
2386823870
// tasks that they spawn should all be batched together, so they should all
2386923871
// receive the same lane.
23870-
if (includesSomeLane(workInProgressRootRenderLanes, OffscreenLane)) {
23872+
// Check the priority of the current render to decide the priority of the
23873+
// deferred task.
23874+
// OffscreenLane is used for prerendering, but we also use OffscreenLane
23875+
// for incremental hydration. It's given the lowest priority because the
23876+
// initial HTML is the same as the final UI. But useDeferredValue during
23877+
// hydration is an exception — we need to upgrade the UI to the final
23878+
// value. So if we're currently hydrating, we treat it like a transition.
23879+
var isPrerendering =
23880+
includesSomeLane(workInProgressRootRenderLanes, OffscreenLane) &&
23881+
!getIsHydrating();
23882+
23883+
if (isPrerendering) {
2387123884
// There's only one OffscreenLane, so if it contains deferred work, we
2387223885
// should just reschedule using the same lane.
23873-
// TODO: We also use OffscreenLane for hydration, on the basis that the
23874-
// initial HTML is the same as the hydrated UI, but since the deferred
23875-
// task will change the UI, it should be treated like an update. Use
23876-
// TransitionHydrationLane to trigger selective hydration.
2387723886
workInProgressDeferredLane = OffscreenLane;
2387823887
} else {
2387923888
// Everything else is spawned as a transition.

compiled/facebook-www/ReactART-prod.classic.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,9 @@ function markSpawnedDeferredLane(root, spawnedLane, entangledLanes) {
579579
var spawnedLaneIndex = 31 - clz32(spawnedLane);
580580
root.entangledLanes |= spawnedLane;
581581
root.entanglements[spawnedLaneIndex] =
582-
root.entanglements[spawnedLaneIndex] | 1073741824 | entangledLanes;
582+
root.entanglements[spawnedLaneIndex] |
583+
1073741824 |
584+
(entangledLanes & 4194218);
583585
}
584586
function markRootEntangled(root, entangledLanes) {
585587
var rootEntangledLanes = (root.entangledLanes |= entangledLanes);
@@ -10179,7 +10181,7 @@ var slice = Array.prototype.slice,
1017910181
return null;
1018010182
},
1018110183
bundleType: 0,
10182-
version: "18.3.0-www-classic-24afbb39",
10184+
version: "18.3.0-www-classic-daa48109",
1018310185
rendererPackageName: "react-art"
1018410186
};
1018510187
var internals$jscomp$inline_1322 = {
@@ -10210,7 +10212,7 @@ var internals$jscomp$inline_1322 = {
1021010212
scheduleRoot: null,
1021110213
setRefreshHandler: null,
1021210214
getCurrentFiber: null,
10213-
reconcilerVersion: "18.3.0-www-classic-24afbb39"
10215+
reconcilerVersion: "18.3.0-www-classic-daa48109"
1021410216
};
1021510217
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
1021610218
var hook$jscomp$inline_1323 = __REACT_DEVTOOLS_GLOBAL_HOOK__;

compiled/facebook-www/ReactART-prod.modern.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,9 @@ function markSpawnedDeferredLane(root, spawnedLane, entangledLanes) {
467467
var spawnedLaneIndex = 31 - clz32(spawnedLane);
468468
root.entangledLanes |= spawnedLane;
469469
root.entanglements[spawnedLaneIndex] =
470-
root.entanglements[spawnedLaneIndex] | 1073741824 | entangledLanes;
470+
root.entanglements[spawnedLaneIndex] |
471+
1073741824 |
472+
(entangledLanes & 4194218);
471473
}
472474
function markRootEntangled(root, entangledLanes) {
473475
var rootEntangledLanes = (root.entangledLanes |= entangledLanes);
@@ -9848,7 +9850,7 @@ var slice = Array.prototype.slice,
98489850
return null;
98499851
},
98509852
bundleType: 0,
9851-
version: "18.3.0-www-modern-27dfd1a3",
9853+
version: "18.3.0-www-modern-5b0eeab3",
98529854
rendererPackageName: "react-art"
98539855
};
98549856
var internals$jscomp$inline_1302 = {
@@ -9879,7 +9881,7 @@ var internals$jscomp$inline_1302 = {
98799881
scheduleRoot: null,
98809882
setRefreshHandler: null,
98819883
getCurrentFiber: null,
9882-
reconcilerVersion: "18.3.0-www-modern-27dfd1a3"
9884+
reconcilerVersion: "18.3.0-www-modern-5b0eeab3"
98839885
};
98849886
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
98859887
var hook$jscomp$inline_1303 = __REACT_DEVTOOLS_GLOBAL_HOOK__;

compiled/facebook-www/ReactDOM-dev.classic.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2460,8 +2460,10 @@ function markSpawnedDeferredLane(root, spawnedLane, entangledLanes) {
24602460
root.entangledLanes |= spawnedLane;
24612461
root.entanglements[spawnedLaneIndex] |=
24622462
DeferredLane | // If the parent render task suspended, we must also entangle those lanes
2463-
// with the spawned task.
2464-
entangledLanes;
2463+
// with the spawned task, so that the deferred task includes all the same
2464+
// updates that the parent task did. We can exclude any lane that is not
2465+
// used for updates (e.g. Offscreen).
2466+
(entangledLanes & UpdateLanes);
24652467
}
24662468

24672469
function markRootEntangled(root, entangledLanes) {
@@ -29689,13 +29691,20 @@ function requestDeferredLane() {
2968929691
// If there are multiple useDeferredValue hooks in the same render, the
2969029692
// tasks that they spawn should all be batched together, so they should all
2969129693
// receive the same lane.
29692-
if (includesSomeLane(workInProgressRootRenderLanes, OffscreenLane)) {
29694+
// Check the priority of the current render to decide the priority of the
29695+
// deferred task.
29696+
// OffscreenLane is used for prerendering, but we also use OffscreenLane
29697+
// for incremental hydration. It's given the lowest priority because the
29698+
// initial HTML is the same as the final UI. But useDeferredValue during
29699+
// hydration is an exception — we need to upgrade the UI to the final
29700+
// value. So if we're currently hydrating, we treat it like a transition.
29701+
var isPrerendering =
29702+
includesSomeLane(workInProgressRootRenderLanes, OffscreenLane) &&
29703+
!getIsHydrating();
29704+
29705+
if (isPrerendering) {
2969329706
// There's only one OffscreenLane, so if it contains deferred work, we
2969429707
// should just reschedule using the same lane.
29695-
// TODO: We also use OffscreenLane for hydration, on the basis that the
29696-
// initial HTML is the same as the hydrated UI, but since the deferred
29697-
// task will change the UI, it should be treated like an update. Use
29698-
// TransitionHydrationLane to trigger selective hydration.
2969929708
workInProgressDeferredLane = OffscreenLane;
2970029709
} else {
2970129710
// Everything else is spawned as a transition.
@@ -34150,7 +34159,7 @@ function createFiberRoot(
3415034159
return root;
3415134160
}
3415234161

34153-
var ReactVersion = "18.3.0-www-classic-a73ccc38";
34162+
var ReactVersion = "18.3.0-www-classic-0db07969";
3415434163

3415534164
function createPortal$1(
3415634165
children,

0 commit comments

Comments
 (0)