Skip to content

Commit

Permalink
Prerendering support for useDeferredValue (#27512)
Browse files Browse the repository at this point in the history
### Based on #27509

Revealing a prerendered tree (hidden -> visible) is considered the same
as mounting a brand new tree. So, when an initialValue argument is
passed to useDeferredValue, and it's prerendered inside a hidden tree,
we should first prerender the initial value.

After the initial value has been prerendered, we switch to prerendering
the final one. This is the same sequence that we use when mounting new
visible tree. Depending on how much prerendering work has been finished
by the time the tree is revealed, we may or may not be able to skip all
the way to the final value.

This means we get the benefits of both prerendering and preview states:
if we have enough resources to prerender the whole thing, we do that. If
we don't, we have a preview state to show for immediate feedback.

DiffTrain build for commit 75c1bd7.
  • Loading branch information
acdlite committed Oct 17, 2023
1 parent b763135 commit 8d3e7a7
Show file tree
Hide file tree
Showing 13 changed files with 342 additions and 295 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<e2046f4b72996278b314c5849a8eb848>>
* @generated SignedSource<<4d9878b267a005902b63980f6c367201>>
*/

'use strict';
Expand Down Expand Up @@ -8580,46 +8580,49 @@ function mountDeferredValueImpl(hook, value, initialValue) {
}

function updateDeferredValueImpl(hook, prevValue, value, initialValue) {
// TODO: We should also check if this component is going from
// hidden -> visible. If so, it should use the initialValue arg.
var shouldDeferValue = !includesOnlyNonUrgentLanes(renderLanes);

if (shouldDeferValue) {
// This is an urgent update. If the value has changed, keep using the
// previous value and spawn a deferred render to update it later.
if (!objectIs(value, prevValue)) {
if (objectIs(value, prevValue)) {
// The incoming value is referentially identical to the currently rendered
// value, so we can bail out quickly.
return value;
} else {
// Received a new value that's different from the current value.
// Check if we're inside a hidden tree
if (isCurrentTreeHidden()) {
// Revealing a prerendered tree is considered the same as mounting new
// one, so we reuse the "mount" path in this case.
var resultValue = mountDeferredValueImpl(hook, value); // Unlike during an actual mount, we need to mark this as an update if
// the value changed.

if (!objectIs(resultValue, prevValue)) {
markWorkInProgressReceivedUpdate();
}

return resultValue;
}

var shouldDeferValue = !includesOnlyNonUrgentLanes(renderLanes);

if (shouldDeferValue) {
// This is an urgent update. Since the value has changed, keep using the
// previous value and spawn a deferred render to update it later.
// Schedule a deferred render
var deferredLane = requestDeferredLane();
currentlyRenderingFiber$1.lanes = mergeLanes(
currentlyRenderingFiber$1.lanes,
deferredLane
);
markSkippedUpdateLanes(deferredLane); // Set this to true to indicate that the rendered value is inconsistent
// from the latest value. The name "baseState" doesn't really match how we
// use it because we're reusing a state hook field instead of creating a
// new one.
markSkippedUpdateLanes(deferredLane); // Reuse the previous value. We do not need to mark this as an update,
// because we did not render a new value.

hook.baseState = true;
} // Reuse the previous value

return prevValue;
} else {
// This is not an urgent update, so we can use the latest value regardless
// of what it is. No need to defer it.
// However, if we're currently inside a spawned render, then we need to mark
// this as an update to prevent the fiber from bailing out.
//
// `baseState` is true when the current value is different from the rendered
// value. The name doesn't really match how we use it because we're reusing
// a state hook field instead of creating a new one.
if (hook.baseState) {
// Flip this back to false.
hook.baseState = false;
return prevValue;
} else {
// This is not an urgent update, so we can use the latest value regardless
// of what it is. No need to defer it.
// Mark this as an update to prevent the fiber from bailing out.
markWorkInProgressReceivedUpdate();
hook.memoizedState = value;
return value;
}

hook.memoizedState = value;
return value;
}
}

Expand Down Expand Up @@ -24875,7 +24878,7 @@ function createFiberRoot(
return root;
}

var ReactVersion = "18.3.0-canary-b2a68a65c-20231017";
var ReactVersion = "18.3.0-canary-75c1bd7ee-20231017";

// Might add PROFILE later.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<d2181e69a294cf0b50b846b598547492>>
* @generated SignedSource<<ed6d8869c24db97c9d90eec277215874>>
*/

"use strict";
Expand Down Expand Up @@ -2849,21 +2849,23 @@ function updateMemo(nextCreate, deps) {
return nextCreate;
}
function updateDeferredValueImpl(hook, prevValue, value) {
if (0 === (renderLanes & 42))
if (objectIs(value, prevValue)) return value;
if (null !== currentTreeHiddenStackCursor.current)
return (
hook.baseState && ((hook.baseState = !1), (didReceiveUpdate = !0)),
(hook.memoizedState = value)
(hook.memoizedState = value),
objectIs(value, prevValue) || (didReceiveUpdate = !0),
value
);
objectIs(value, prevValue) ||
(0 === workInProgressDeferredLane &&
(workInProgressDeferredLane =
0 !== (workInProgressRootRenderLanes & 536870912)
? 536870912
: requestTransitionLane()),
(value = workInProgressDeferredLane),
(currentlyRenderingFiber$1.lanes |= value),
(workInProgressRootSkippedLanes |= value),
(hook.baseState = !0));
if (0 === (renderLanes & 42))
return (didReceiveUpdate = !0), (hook.memoizedState = value);
0 === workInProgressDeferredLane &&
(workInProgressDeferredLane =
0 !== (workInProgressRootRenderLanes & 536870912)
? 536870912
: requestTransitionLane());
hook = workInProgressDeferredLane;
currentlyRenderingFiber$1.lanes |= hook;
workInProgressRootSkippedLanes |= hook;
return prevValue;
}
function startTransition(fiber, queue, pendingState, finishedState, callback) {
Expand Down Expand Up @@ -9015,19 +9017,19 @@ function wrapFiber(fiber) {
fiberToWrapper.set(fiber, wrapper));
return wrapper;
}
var devToolsConfig$jscomp$inline_1025 = {
var devToolsConfig$jscomp$inline_1031 = {
findFiberByHostInstance: function () {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "18.3.0-canary-b2a68a65c-20231017",
version: "18.3.0-canary-75c1bd7ee-20231017",
rendererPackageName: "react-test-renderer"
};
var internals$jscomp$inline_1218 = {
bundleType: devToolsConfig$jscomp$inline_1025.bundleType,
version: devToolsConfig$jscomp$inline_1025.version,
rendererPackageName: devToolsConfig$jscomp$inline_1025.rendererPackageName,
rendererConfig: devToolsConfig$jscomp$inline_1025.rendererConfig,
var internals$jscomp$inline_1224 = {
bundleType: devToolsConfig$jscomp$inline_1031.bundleType,
version: devToolsConfig$jscomp$inline_1031.version,
rendererPackageName: devToolsConfig$jscomp$inline_1031.rendererPackageName,
rendererConfig: devToolsConfig$jscomp$inline_1031.rendererConfig,
overrideHookState: null,
overrideHookStateDeletePath: null,
overrideHookStateRenamePath: null,
Expand All @@ -9044,26 +9046,26 @@ var internals$jscomp$inline_1218 = {
return null === fiber ? null : fiber.stateNode;
},
findFiberByHostInstance:
devToolsConfig$jscomp$inline_1025.findFiberByHostInstance ||
devToolsConfig$jscomp$inline_1031.findFiberByHostInstance ||
emptyFindFiberByHostInstance,
findHostInstancesForRefresh: null,
scheduleRefresh: null,
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-canary-b2a68a65c-20231017"
reconcilerVersion: "18.3.0-canary-75c1bd7ee-20231017"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1219 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
var hook$jscomp$inline_1225 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
if (
!hook$jscomp$inline_1219.isDisabled &&
hook$jscomp$inline_1219.supportsFiber
!hook$jscomp$inline_1225.isDisabled &&
hook$jscomp$inline_1225.supportsFiber
)
try {
(rendererID = hook$jscomp$inline_1219.inject(
internals$jscomp$inline_1218
(rendererID = hook$jscomp$inline_1225.inject(
internals$jscomp$inline_1224
)),
(injectedHook = hook$jscomp$inline_1219);
(injectedHook = hook$jscomp$inline_1225);
} catch (err) {}
}
exports._Scheduler = Scheduler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<efc6d41dec65b2187ec52681d760c9b7>>
* @generated SignedSource<<c3d1140631a9ffbb19f029e3ecdb90fc>>
*/

"use strict";
Expand Down Expand Up @@ -2869,21 +2869,23 @@ function updateMemo(nextCreate, deps) {
return nextCreate;
}
function updateDeferredValueImpl(hook, prevValue, value) {
if (0 === (renderLanes & 42))
if (objectIs(value, prevValue)) return value;
if (null !== currentTreeHiddenStackCursor.current)
return (
hook.baseState && ((hook.baseState = !1), (didReceiveUpdate = !0)),
(hook.memoizedState = value)
(hook.memoizedState = value),
objectIs(value, prevValue) || (didReceiveUpdate = !0),
value
);
objectIs(value, prevValue) ||
(0 === workInProgressDeferredLane &&
(workInProgressDeferredLane =
0 !== (workInProgressRootRenderLanes & 536870912)
? 536870912
: requestTransitionLane()),
(value = workInProgressDeferredLane),
(currentlyRenderingFiber$1.lanes |= value),
(workInProgressRootSkippedLanes |= value),
(hook.baseState = !0));
if (0 === (renderLanes & 42))
return (didReceiveUpdate = !0), (hook.memoizedState = value);
0 === workInProgressDeferredLane &&
(workInProgressDeferredLane =
0 !== (workInProgressRootRenderLanes & 536870912)
? 536870912
: requestTransitionLane());
hook = workInProgressDeferredLane;
currentlyRenderingFiber$1.lanes |= hook;
workInProgressRootSkippedLanes |= hook;
return prevValue;
}
function startTransition(fiber, queue, pendingState, finishedState, callback) {
Expand Down Expand Up @@ -9441,19 +9443,19 @@ function wrapFiber(fiber) {
fiberToWrapper.set(fiber, wrapper));
return wrapper;
}
var devToolsConfig$jscomp$inline_1067 = {
var devToolsConfig$jscomp$inline_1073 = {
findFiberByHostInstance: function () {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "18.3.0-canary-b2a68a65c-20231017",
version: "18.3.0-canary-75c1bd7ee-20231017",
rendererPackageName: "react-test-renderer"
};
var internals$jscomp$inline_1259 = {
bundleType: devToolsConfig$jscomp$inline_1067.bundleType,
version: devToolsConfig$jscomp$inline_1067.version,
rendererPackageName: devToolsConfig$jscomp$inline_1067.rendererPackageName,
rendererConfig: devToolsConfig$jscomp$inline_1067.rendererConfig,
var internals$jscomp$inline_1265 = {
bundleType: devToolsConfig$jscomp$inline_1073.bundleType,
version: devToolsConfig$jscomp$inline_1073.version,
rendererPackageName: devToolsConfig$jscomp$inline_1073.rendererPackageName,
rendererConfig: devToolsConfig$jscomp$inline_1073.rendererConfig,
overrideHookState: null,
overrideHookStateDeletePath: null,
overrideHookStateRenamePath: null,
Expand All @@ -9470,26 +9472,26 @@ var internals$jscomp$inline_1259 = {
return null === fiber ? null : fiber.stateNode;
},
findFiberByHostInstance:
devToolsConfig$jscomp$inline_1067.findFiberByHostInstance ||
devToolsConfig$jscomp$inline_1073.findFiberByHostInstance ||
emptyFindFiberByHostInstance,
findHostInstancesForRefresh: null,
scheduleRefresh: null,
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-canary-b2a68a65c-20231017"
reconcilerVersion: "18.3.0-canary-75c1bd7ee-20231017"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1260 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
var hook$jscomp$inline_1266 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
if (
!hook$jscomp$inline_1260.isDisabled &&
hook$jscomp$inline_1260.supportsFiber
!hook$jscomp$inline_1266.isDisabled &&
hook$jscomp$inline_1266.supportsFiber
)
try {
(rendererID = hook$jscomp$inline_1260.inject(
internals$jscomp$inline_1259
(rendererID = hook$jscomp$inline_1266.inject(
internals$jscomp$inline_1265
)),
(injectedHook = hook$jscomp$inline_1260);
(injectedHook = hook$jscomp$inline_1266);
} catch (err) {}
}
exports._Scheduler = Scheduler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ if (
}
"use strict";

var ReactVersion = "18.3.0-canary-b2a68a65c-20231017";
var ReactVersion = "18.3.0-canary-75c1bd7ee-20231017";

// ATTENTION
// When adding new symbols to this file,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -580,4 +580,4 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactCurrentDispatcher.current.useTransition();
};
exports.version = "18.3.0-canary-b2a68a65c-20231017";
exports.version = "18.3.0-canary-75c1bd7ee-20231017";
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactCurrentDispatcher.current.useTransition();
};
exports.version = "18.3.0-canary-b2a68a65c-20231017";
exports.version = "18.3.0-canary-75c1bd7ee-20231017";

/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
if (
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
b2a68a65c84b63ac86930d88ae5c84380cbbdeb6
75c1bd7ee7e4a87a4dd0f560e157c57957ef823b
Loading

0 comments on commit 8d3e7a7

Please sign in to comment.