Skip to content

Commit

Permalink
Fix uSES hydration in strict mode (#26791)
Browse files Browse the repository at this point in the history
Previously, we'd call and use getSnapshot on the second render resulting
in `Warning: Text content did not match. Server: "Nay!" Client: "Yay!"`
and then `Error: Text content does not match server-rendered HTML.`.

Fixes #26095. Closes #26113. Closes #25650.

---------

Co-authored-by: eps1lon <[email protected]>

DiffTrain build for [4cd7065](4cd7065)
  • Loading branch information
sophiebits committed May 12, 2023
1 parent d08f005 commit 646a30e
Show file tree
Hide file tree
Showing 18 changed files with 364 additions and 230 deletions.
2 changes: 1 addition & 1 deletion compiled/facebook-www/REVISION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
df12d7eac40c87bd5fdde0aa5a739bce9e7dce27
4cd7065665ea2cf33c306265c8d817904bb401ca
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-dev.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ if (
}
"use strict";

var ReactVersion = "18.3.0-www-modern-bc9d666a";
var ReactVersion = "18.3.0-www-modern-6995898a";

// ATTENTION
// When adding new symbols to this file,
Expand Down
29 changes: 16 additions & 13 deletions compiled/facebook-www/ReactART-dev.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function _assertThisInitialized(self) {
return self;
}

var ReactVersion = "18.3.0-www-classic-e1e0fe51";
var ReactVersion = "18.3.0-www-classic-d297891e";

var LegacyRoot = 0;
var ConcurrentRoot = 1;
Expand Down Expand Up @@ -9250,8 +9250,6 @@ function mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
// clean-up function, and we track the deps correctly, we can call pushEffect
// directly, without storing any additional state. For the same reason, we
// don't need to set a static flag, either.
// TODO: We can move this to the passive phase once we add a pre-commit
// consistency check. See the next comment.

fiber.flags |= Passive$1;
pushEffect(
Expand All @@ -9269,18 +9267,22 @@ function updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
// normal rules of React, and only works because store updates are
// always synchronous.

var nextSnapshot = getSnapshot();
var nextSnapshot;

{
if (!didWarnUncachedGetSnapshot) {
var cachedSnapshot = getSnapshot();
nextSnapshot = getSnapshot();

if (!objectIs(nextSnapshot, cachedSnapshot)) {
error(
"The result of getSnapshot should be cached to avoid an infinite loop"
);
{
if (!didWarnUncachedGetSnapshot) {
var cachedSnapshot = getSnapshot();

didWarnUncachedGetSnapshot = true;
if (!objectIs(nextSnapshot, cachedSnapshot)) {
error(
"The result of getSnapshot should be cached to avoid an infinite loop"
);

didWarnUncachedGetSnapshot = true;
}
}
}
}
Expand All @@ -9303,7 +9305,7 @@ function updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {

if (
inst.getSnapshot !== getSnapshot ||
snapshotChanged || // Check if the susbcribe function changed. We can save some memory by
snapshotChanged || // Check if the subscribe function changed. We can save some memory by
// checking whether we scheduled a subscription effect above.
(workInProgressHook !== null &&
workInProgressHook.memoizedState.tag & HasEffect)
Expand Down Expand Up @@ -9645,7 +9647,8 @@ function updateEffectImpl(fiberFlags, hookFlags, create, deps) {
var hook = updateWorkInProgressHook();
var nextDeps = deps === undefined ? null : deps;
var effect = hook.memoizedState;
var inst = effect.inst; // currentHook is null when rerendering after a render phase state update.
var inst = effect.inst; // currentHook is null on initial mount when rerendering after a render phase
// state update or for strict mode.

if (currentHook !== null) {
if (nextDeps !== null) {
Expand Down
29 changes: 16 additions & 13 deletions compiled/facebook-www/ReactART-dev.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function _assertThisInitialized(self) {
return self;
}

var ReactVersion = "18.3.0-www-modern-f0df93d6";
var ReactVersion = "18.3.0-www-modern-2f2a7948";

var LegacyRoot = 0;
var ConcurrentRoot = 1;
Expand Down Expand Up @@ -9006,8 +9006,6 @@ function mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
// clean-up function, and we track the deps correctly, we can call pushEffect
// directly, without storing any additional state. For the same reason, we
// don't need to set a static flag, either.
// TODO: We can move this to the passive phase once we add a pre-commit
// consistency check. See the next comment.

fiber.flags |= Passive$1;
pushEffect(
Expand All @@ -9025,18 +9023,22 @@ function updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
// normal rules of React, and only works because store updates are
// always synchronous.

var nextSnapshot = getSnapshot();
var nextSnapshot;

{
if (!didWarnUncachedGetSnapshot) {
var cachedSnapshot = getSnapshot();
nextSnapshot = getSnapshot();

if (!objectIs(nextSnapshot, cachedSnapshot)) {
error(
"The result of getSnapshot should be cached to avoid an infinite loop"
);
{
if (!didWarnUncachedGetSnapshot) {
var cachedSnapshot = getSnapshot();

didWarnUncachedGetSnapshot = true;
if (!objectIs(nextSnapshot, cachedSnapshot)) {
error(
"The result of getSnapshot should be cached to avoid an infinite loop"
);

didWarnUncachedGetSnapshot = true;
}
}
}
}
Expand All @@ -9059,7 +9061,7 @@ function updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {

if (
inst.getSnapshot !== getSnapshot ||
snapshotChanged || // Check if the susbcribe function changed. We can save some memory by
snapshotChanged || // Check if the subscribe function changed. We can save some memory by
// checking whether we scheduled a subscription effect above.
(workInProgressHook !== null &&
workInProgressHook.memoizedState.tag & HasEffect)
Expand Down Expand Up @@ -9401,7 +9403,8 @@ function updateEffectImpl(fiberFlags, hookFlags, create, deps) {
var hook = updateWorkInProgressHook();
var nextDeps = deps === undefined ? null : deps;
var effect = hook.memoizedState;
var inst = effect.inst; // currentHook is null when rerendering after a render phase state update.
var inst = effect.inst; // currentHook is null on initial mount when rerendering after a render phase
// state update or for strict mode.

if (currentHook !== null) {
if (nextDeps !== null) {
Expand Down
16 changes: 8 additions & 8 deletions compiled/facebook-www/ReactART-prod.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -2971,12 +2971,12 @@ function updateMutableSource(source, getSnapshot, subscribe) {
}
function updateSyncExternalStore(subscribe, getSnapshot) {
var fiber = currentlyRenderingFiber$1,
hook = updateWorkInProgressHook(),
nextSnapshot = getSnapshot(),
snapshotChanged = !objectIs(
(currentHook || hook).memoizedState,
nextSnapshot
);
hook = updateWorkInProgressHook();
var nextSnapshot = getSnapshot();
var snapshotChanged = !objectIs(
(currentHook || hook).memoizedState,
nextSnapshot
);
snapshotChanged &&
((hook.memoizedState = nextSnapshot), (didReceiveUpdate = !0));
hook = hook.queue;
Expand Down Expand Up @@ -10193,7 +10193,7 @@ var slice = Array.prototype.slice,
return null;
},
bundleType: 0,
version: "18.3.0-www-classic-b2a7bab7",
version: "18.3.0-www-classic-06adf47d",
rendererPackageName: "react-art"
};
var internals$jscomp$inline_1325 = {
Expand Down Expand Up @@ -10224,7 +10224,7 @@ var internals$jscomp$inline_1325 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-www-classic-b2a7bab7"
reconcilerVersion: "18.3.0-www-classic-06adf47d"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1326 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
Expand Down
16 changes: 8 additions & 8 deletions compiled/facebook-www/ReactART-prod.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -2777,12 +2777,12 @@ function updateMutableSource(source, getSnapshot, subscribe) {
}
function updateSyncExternalStore(subscribe, getSnapshot) {
var fiber = currentlyRenderingFiber$1,
hook = updateWorkInProgressHook(),
nextSnapshot = getSnapshot(),
snapshotChanged = !objectIs(
(currentHook || hook).memoizedState,
nextSnapshot
);
hook = updateWorkInProgressHook();
var nextSnapshot = getSnapshot();
var snapshotChanged = !objectIs(
(currentHook || hook).memoizedState,
nextSnapshot
);
snapshotChanged &&
((hook.memoizedState = nextSnapshot), (didReceiveUpdate = !0));
hook = hook.queue;
Expand Down Expand Up @@ -9858,7 +9858,7 @@ var slice = Array.prototype.slice,
return null;
},
bundleType: 0,
version: "18.3.0-www-modern-50ab1d1d",
version: "18.3.0-www-modern-899090e3",
rendererPackageName: "react-art"
};
var internals$jscomp$inline_1305 = {
Expand Down Expand Up @@ -9889,7 +9889,7 @@ var internals$jscomp$inline_1305 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-www-modern-50ab1d1d"
reconcilerVersion: "18.3.0-www-modern-899090e3"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1306 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
Expand Down
52 changes: 33 additions & 19 deletions compiled/facebook-www/ReactDOM-dev.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -13799,8 +13799,6 @@ function mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
// clean-up function, and we track the deps correctly, we can call pushEffect
// directly, without storing any additional state. For the same reason, we
// don't need to set a static flag, either.
// TODO: We can move this to the passive phase once we add a pre-commit
// consistency check. See the next comment.

fiber.flags |= Passive$1;
pushEffect(
Expand All @@ -13818,18 +13816,33 @@ function updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
// normal rules of React, and only works because store updates are
// always synchronous.

var nextSnapshot = getSnapshot();
var nextSnapshot;
var isHydrating = getIsHydrating();

{
if (!didWarnUncachedGetSnapshot) {
var cachedSnapshot = getSnapshot();
if (isHydrating) {
// Needed for strict mode double render
if (getServerSnapshot === undefined) {
throw new Error(
"Missing getServerSnapshot, which is required for " +
"server-rendered content. Will revert to client rendering."
);
}

if (!objectIs(nextSnapshot, cachedSnapshot)) {
error(
"The result of getSnapshot should be cached to avoid an infinite loop"
);
nextSnapshot = getServerSnapshot();
} else {
nextSnapshot = getSnapshot();

didWarnUncachedGetSnapshot = true;
{
if (!didWarnUncachedGetSnapshot) {
var cachedSnapshot = getSnapshot();

if (!objectIs(nextSnapshot, cachedSnapshot)) {
error(
"The result of getSnapshot should be cached to avoid an infinite loop"
);

didWarnUncachedGetSnapshot = true;
}
}
}
}
Expand All @@ -13852,7 +13865,7 @@ function updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {

if (
inst.getSnapshot !== getSnapshot ||
snapshotChanged || // Check if the susbcribe function changed. We can save some memory by
snapshotChanged || // Check if the subscribe function changed. We can save some memory by
// checking whether we scheduled a subscription effect above.
(workInProgressHook !== null &&
workInProgressHook.memoizedState.tag & HasEffect)
Expand All @@ -13875,7 +13888,7 @@ function updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
);
}

if (!includesBlockingLane(root, renderLanes$1)) {
if (!isHydrating && !includesBlockingLane(root, renderLanes$1)) {
pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
}
}
Expand Down Expand Up @@ -14194,7 +14207,8 @@ function updateEffectImpl(fiberFlags, hookFlags, create, deps) {
var hook = updateWorkInProgressHook();
var nextDeps = deps === undefined ? null : deps;
var effect = hook.memoizedState;
var inst = effect.inst; // currentHook is null when rerendering after a render phase state update.
var inst = effect.inst; // currentHook is null on initial mount when rerendering after a render phase
// state update or for strict mode.

if (currentHook !== null) {
if (nextDeps !== null) {
Expand Down Expand Up @@ -15443,7 +15457,7 @@ var InvalidNestedHooksDispatcherOnRerenderInDEV = null;
useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
currentHookNameInDev = "useSyncExternalStore";
updateHookTypesDev();
return updateSyncExternalStore(subscribe, getSnapshot);
return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
},
useId: function () {
currentHookNameInDev = "useId";
Expand Down Expand Up @@ -15587,7 +15601,7 @@ var InvalidNestedHooksDispatcherOnRerenderInDEV = null;
useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
currentHookNameInDev = "useSyncExternalStore";
updateHookTypesDev();
return updateSyncExternalStore(subscribe, getSnapshot);
return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
},
useId: function () {
currentHookNameInDev = "useId";
Expand Down Expand Up @@ -15918,7 +15932,7 @@ var InvalidNestedHooksDispatcherOnRerenderInDEV = null;
currentHookNameInDev = "useSyncExternalStore";
warnInvalidHookAccess();
updateHookTypesDev();
return updateSyncExternalStore(subscribe, getSnapshot);
return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
},
useId: function () {
currentHookNameInDev = "useId";
Expand Down Expand Up @@ -16085,7 +16099,7 @@ var InvalidNestedHooksDispatcherOnRerenderInDEV = null;
currentHookNameInDev = "useSyncExternalStore";
warnInvalidHookAccess();
updateHookTypesDev();
return updateSyncExternalStore(subscribe, getSnapshot);
return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
},
useId: function () {
currentHookNameInDev = "useId";
Expand Down Expand Up @@ -34168,7 +34182,7 @@ function createFiberRoot(
return root;
}

var ReactVersion = "18.3.0-www-classic-b6d18d0e";
var ReactVersion = "18.3.0-www-classic-340ebf62";

function createPortal$1(
children,
Expand Down
Loading

0 comments on commit 646a30e

Please sign in to comment.