mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Fix: Suspend while recovering from hydration error (#28800)
Fixes a bug that happens when an error occurs during hydration, React switches to client rendering, and then the client render suspends. It works correctly if there's a Suspense boundary on the stack, but not if it happens in the shell of the app. Prior to this fix, the app would crash with an "Unknown root exit status" error. I left a TODO comment for how we might refactor this code to be less confusing in the future. DiffTrain build for commit https://github.com/facebook/react/commit/3f9e237a2feb74f1fca23b76d9d2e9e1713e2ba1.
This commit is contained in:
+17
-6
@@ -7,7 +7,7 @@
|
||||
* @noflow
|
||||
* @nolint
|
||||
* @preventMunge
|
||||
* @generated SignedSource<<fdd11cfd0a9f56383a8f5fff2c50fc9e>>
|
||||
* @generated SignedSource<<881e78352310ebeea1f473a89441e0c9>>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -22773,20 +22773,31 @@ if (__DEV__) {
|
||||
} // Check if something threw
|
||||
|
||||
if (exitStatus === RootErrored) {
|
||||
var originallyAttemptedLanes = lanes;
|
||||
var lanesThatJustErrored = lanes;
|
||||
var errorRetryLanes = getLanesToRetrySynchronouslyOnError(
|
||||
root,
|
||||
originallyAttemptedLanes
|
||||
lanesThatJustErrored
|
||||
);
|
||||
|
||||
if (errorRetryLanes !== NoLanes) {
|
||||
lanes = errorRetryLanes;
|
||||
exitStatus = recoverFromConcurrentError(
|
||||
root,
|
||||
originallyAttemptedLanes,
|
||||
lanesThatJustErrored,
|
||||
errorRetryLanes
|
||||
);
|
||||
renderWasConcurrent = false;
|
||||
renderWasConcurrent = false; // Need to check the exit status again.
|
||||
|
||||
if (exitStatus !== RootErrored) {
|
||||
// The root did not error this time. Restart the exit algorithm
|
||||
// from the beginning.
|
||||
// TODO: Refactor the exit algorithm to be less confusing. Maybe
|
||||
// more branches + recursion instead of a loop. I think the only
|
||||
// thing that causes it to be a loop is the RootDidNotComplete
|
||||
// check. If that's true, then we don't need a loop/recursion
|
||||
// at all.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26590,7 +26601,7 @@ if (__DEV__) {
|
||||
return root;
|
||||
}
|
||||
|
||||
var ReactVersion = "19.0.0-canary-27752dcf";
|
||||
var ReactVersion = "19.0.0-canary-89546c49";
|
||||
|
||||
/*
|
||||
* The `'' + value` pattern (used in perf-sensitive code) throws for Symbol
|
||||
|
||||
+10
-5
@@ -7,7 +7,7 @@
|
||||
* @noflow
|
||||
* @nolint
|
||||
* @preventMunge
|
||||
* @generated SignedSource<<761db320e8f45126e2feb86ac8bca704>>
|
||||
* @generated SignedSource<<5159750d4ca65c393d679d370a71d7cb>>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -7472,13 +7472,18 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
|
||||
root,
|
||||
renderWasConcurrent
|
||||
);
|
||||
0 !== errorRetryLanes &&
|
||||
if (
|
||||
0 !== errorRetryLanes &&
|
||||
((lanes = errorRetryLanes),
|
||||
(exitStatus = recoverFromConcurrentError(
|
||||
root,
|
||||
renderWasConcurrent,
|
||||
errorRetryLanes
|
||||
)));
|
||||
)),
|
||||
(renderWasConcurrent = !1),
|
||||
2 !== exitStatus)
|
||||
)
|
||||
continue;
|
||||
}
|
||||
if (1 === exitStatus) {
|
||||
prepareFreshStack(root, 0);
|
||||
@@ -9119,7 +9124,7 @@ var devToolsConfig$jscomp$inline_1019 = {
|
||||
throw Error("TestRenderer does not support findFiberByHostInstance()");
|
||||
},
|
||||
bundleType: 0,
|
||||
version: "19.0.0-canary-3d34fdc4",
|
||||
version: "19.0.0-canary-df9de31a",
|
||||
rendererPackageName: "react-test-renderer"
|
||||
};
|
||||
var internals$jscomp$inline_1238 = {
|
||||
@@ -9150,7 +9155,7 @@ var internals$jscomp$inline_1238 = {
|
||||
scheduleRoot: null,
|
||||
setRefreshHandler: null,
|
||||
getCurrentFiber: null,
|
||||
reconcilerVersion: "19.0.0-canary-3d34fdc4"
|
||||
reconcilerVersion: "19.0.0-canary-df9de31a"
|
||||
};
|
||||
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
|
||||
var hook$jscomp$inline_1239 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
|
||||
+10
-5
@@ -7,7 +7,7 @@
|
||||
* @noflow
|
||||
* @nolint
|
||||
* @preventMunge
|
||||
* @generated SignedSource<<ec92ba0d6674d69f102b6b07bcc607e9>>
|
||||
* @generated SignedSource<<21f520424802b9edb8fc0a8c96178e1c>>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -7966,13 +7966,18 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
|
||||
root,
|
||||
renderWasConcurrent
|
||||
);
|
||||
0 !== errorRetryLanes &&
|
||||
if (
|
||||
0 !== errorRetryLanes &&
|
||||
((lanes = errorRetryLanes),
|
||||
(exitStatus = recoverFromConcurrentError(
|
||||
root,
|
||||
renderWasConcurrent,
|
||||
errorRetryLanes
|
||||
)));
|
||||
)),
|
||||
(renderWasConcurrent = !1),
|
||||
2 !== exitStatus)
|
||||
)
|
||||
continue;
|
||||
}
|
||||
if (1 === exitStatus) {
|
||||
prepareFreshStack(root, 0);
|
||||
@@ -9757,7 +9762,7 @@ var devToolsConfig$jscomp$inline_1082 = {
|
||||
throw Error("TestRenderer does not support findFiberByHostInstance()");
|
||||
},
|
||||
bundleType: 0,
|
||||
version: "19.0.0-canary-09ccb82c",
|
||||
version: "19.0.0-canary-12ee58d1",
|
||||
rendererPackageName: "react-test-renderer"
|
||||
};
|
||||
(function (internals) {
|
||||
@@ -9801,7 +9806,7 @@ var devToolsConfig$jscomp$inline_1082 = {
|
||||
scheduleRoot: null,
|
||||
setRefreshHandler: null,
|
||||
getCurrentFiber: null,
|
||||
reconcilerVersion: "19.0.0-canary-09ccb82c"
|
||||
reconcilerVersion: "19.0.0-canary-12ee58d1"
|
||||
});
|
||||
exports._Scheduler = Scheduler;
|
||||
exports.act = act;
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
64c8d2d45d49dbb2f59ea23e5e739eb79e124abc
|
||||
3f9e237a2feb74f1fca23b76d9d2e9e1713e2ba1
|
||||
|
||||
+17
-6
@@ -7,7 +7,7 @@
|
||||
* @noflow
|
||||
* @nolint
|
||||
* @preventMunge
|
||||
* @generated SignedSource<<494d27c89fdf2fb47e01cb552bf35a07>>
|
||||
* @generated SignedSource<<a4571cdfd524cd72b685d2a3adeafd2c>>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -26242,20 +26242,31 @@ to return true:wantsResponderID| |
|
||||
} // Check if something threw
|
||||
|
||||
if (exitStatus === RootErrored) {
|
||||
var originallyAttemptedLanes = lanes;
|
||||
var lanesThatJustErrored = lanes;
|
||||
var errorRetryLanes = getLanesToRetrySynchronouslyOnError(
|
||||
root,
|
||||
originallyAttemptedLanes
|
||||
lanesThatJustErrored
|
||||
);
|
||||
|
||||
if (errorRetryLanes !== NoLanes) {
|
||||
lanes = errorRetryLanes;
|
||||
exitStatus = recoverFromConcurrentError(
|
||||
root,
|
||||
originallyAttemptedLanes,
|
||||
lanesThatJustErrored,
|
||||
errorRetryLanes
|
||||
);
|
||||
renderWasConcurrent = false;
|
||||
renderWasConcurrent = false; // Need to check the exit status again.
|
||||
|
||||
if (exitStatus !== RootErrored) {
|
||||
// The root did not error this time. Restart the exit algorithm
|
||||
// from the beginning.
|
||||
// TODO: Refactor the exit algorithm to be less confusing. Maybe
|
||||
// more branches + recursion instead of a loop. I think the only
|
||||
// thing that causes it to be a loop is the RootDidNotComplete
|
||||
// check. If that's true, then we don't need a loop/recursion
|
||||
// at all.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30237,7 +30248,7 @@ to return true:wantsResponderID| |
|
||||
return root;
|
||||
}
|
||||
|
||||
var ReactVersion = "19.0.0-canary-d3b8f5e2";
|
||||
var ReactVersion = "19.0.0-canary-704f6d1b";
|
||||
|
||||
/*
|
||||
* The `'' + value` pattern (used in perf-sensitive code) throws for Symbol
|
||||
|
||||
+10
-5
@@ -7,7 +7,7 @@
|
||||
* @noflow
|
||||
* @nolint
|
||||
* @preventMunge
|
||||
* @generated SignedSource<<16efbcb0da2cbd078a40efb1a0552fe8>>
|
||||
* @generated SignedSource<<2e33e857cb3eee98d5e57d1509811f8c>>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -9055,13 +9055,18 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
|
||||
root,
|
||||
renderWasConcurrent
|
||||
);
|
||||
0 !== errorRetryLanes &&
|
||||
if (
|
||||
0 !== errorRetryLanes &&
|
||||
((lanes = errorRetryLanes),
|
||||
(exitStatus = recoverFromConcurrentError(
|
||||
root,
|
||||
renderWasConcurrent,
|
||||
errorRetryLanes
|
||||
)));
|
||||
)),
|
||||
(renderWasConcurrent = !1),
|
||||
2 !== exitStatus)
|
||||
)
|
||||
continue;
|
||||
}
|
||||
if (1 === exitStatus) {
|
||||
prepareFreshStack(root, 0);
|
||||
@@ -10572,7 +10577,7 @@ var roots = new Map(),
|
||||
devToolsConfig$jscomp$inline_1099 = {
|
||||
findFiberByHostInstance: getInstanceFromNode,
|
||||
bundleType: 0,
|
||||
version: "19.0.0-canary-120922b5",
|
||||
version: "19.0.0-canary-a19c8da4",
|
||||
rendererPackageName: "react-native-renderer",
|
||||
rendererConfig: {
|
||||
getInspectorDataForInstance: getInspectorDataForInstance,
|
||||
@@ -10615,7 +10620,7 @@ var internals$jscomp$inline_1366 = {
|
||||
scheduleRoot: null,
|
||||
setRefreshHandler: null,
|
||||
getCurrentFiber: null,
|
||||
reconcilerVersion: "19.0.0-canary-120922b5"
|
||||
reconcilerVersion: "19.0.0-canary-a19c8da4"
|
||||
};
|
||||
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
|
||||
var hook$jscomp$inline_1367 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
|
||||
+10
-5
@@ -7,7 +7,7 @@
|
||||
* @noflow
|
||||
* @nolint
|
||||
* @preventMunge
|
||||
* @generated SignedSource<<a3c25968a30f5f8cd268a3314ac130fe>>
|
||||
* @generated SignedSource<<5fa81808919e6dc188f7876d0baea787>>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -9585,13 +9585,18 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
|
||||
root,
|
||||
renderWasConcurrent
|
||||
);
|
||||
0 !== errorRetryLanes &&
|
||||
if (
|
||||
0 !== errorRetryLanes &&
|
||||
((lanes = errorRetryLanes),
|
||||
(exitStatus = recoverFromConcurrentError(
|
||||
root,
|
||||
renderWasConcurrent,
|
||||
errorRetryLanes
|
||||
)));
|
||||
)),
|
||||
(renderWasConcurrent = !1),
|
||||
2 !== exitStatus)
|
||||
)
|
||||
continue;
|
||||
}
|
||||
if (1 === exitStatus) {
|
||||
prepareFreshStack(root, 0);
|
||||
@@ -11277,7 +11282,7 @@ var roots = new Map(),
|
||||
devToolsConfig$jscomp$inline_1179 = {
|
||||
findFiberByHostInstance: getInstanceFromNode,
|
||||
bundleType: 0,
|
||||
version: "19.0.0-canary-7ffb2ad4",
|
||||
version: "19.0.0-canary-0d80c3d3",
|
||||
rendererPackageName: "react-native-renderer",
|
||||
rendererConfig: {
|
||||
getInspectorDataForInstance: getInspectorDataForInstance,
|
||||
@@ -11333,7 +11338,7 @@ var roots = new Map(),
|
||||
scheduleRoot: null,
|
||||
setRefreshHandler: null,
|
||||
getCurrentFiber: null,
|
||||
reconcilerVersion: "19.0.0-canary-7ffb2ad4"
|
||||
reconcilerVersion: "19.0.0-canary-0d80c3d3"
|
||||
});
|
||||
exports.createPortal = function (children, containerTag) {
|
||||
return createPortal$1(
|
||||
|
||||
+17
-6
@@ -7,7 +7,7 @@
|
||||
* @noflow
|
||||
* @nolint
|
||||
* @preventMunge
|
||||
* @generated SignedSource<<24fee48abc3bddda74443b28d17bbc3e>>
|
||||
* @generated SignedSource<<a64d4ff9138061f9c517e2327f49b803>>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -26682,20 +26682,31 @@ to return true:wantsResponderID| |
|
||||
} // Check if something threw
|
||||
|
||||
if (exitStatus === RootErrored) {
|
||||
var originallyAttemptedLanes = lanes;
|
||||
var lanesThatJustErrored = lanes;
|
||||
var errorRetryLanes = getLanesToRetrySynchronouslyOnError(
|
||||
root,
|
||||
originallyAttemptedLanes
|
||||
lanesThatJustErrored
|
||||
);
|
||||
|
||||
if (errorRetryLanes !== NoLanes) {
|
||||
lanes = errorRetryLanes;
|
||||
exitStatus = recoverFromConcurrentError(
|
||||
root,
|
||||
originallyAttemptedLanes,
|
||||
lanesThatJustErrored,
|
||||
errorRetryLanes
|
||||
);
|
||||
renderWasConcurrent = false;
|
||||
renderWasConcurrent = false; // Need to check the exit status again.
|
||||
|
||||
if (exitStatus !== RootErrored) {
|
||||
// The root did not error this time. Restart the exit algorithm
|
||||
// from the beginning.
|
||||
// TODO: Refactor the exit algorithm to be less confusing. Maybe
|
||||
// more branches + recursion instead of a loop. I think the only
|
||||
// thing that causes it to be a loop is the RootDidNotComplete
|
||||
// check. If that's true, then we don't need a loop/recursion
|
||||
// at all.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30677,7 +30688,7 @@ to return true:wantsResponderID| |
|
||||
return root;
|
||||
}
|
||||
|
||||
var ReactVersion = "19.0.0-canary-ae39878d";
|
||||
var ReactVersion = "19.0.0-canary-f860c0a2";
|
||||
|
||||
/*
|
||||
* The `'' + value` pattern (used in perf-sensitive code) throws for Symbol
|
||||
|
||||
+10
-5
@@ -7,7 +7,7 @@
|
||||
* @noflow
|
||||
* @nolint
|
||||
* @preventMunge
|
||||
* @generated SignedSource<<9314995819fee277c04cd34d21bec2c4>>
|
||||
* @generated SignedSource<<be6315c6d94122ce59e8cac641a06317>>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -9267,13 +9267,18 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
|
||||
root,
|
||||
renderWasConcurrent
|
||||
);
|
||||
0 !== errorRetryLanes &&
|
||||
if (
|
||||
0 !== errorRetryLanes &&
|
||||
((lanes = errorRetryLanes),
|
||||
(exitStatus = recoverFromConcurrentError(
|
||||
root,
|
||||
renderWasConcurrent,
|
||||
errorRetryLanes
|
||||
)));
|
||||
)),
|
||||
(renderWasConcurrent = !1),
|
||||
2 !== exitStatus)
|
||||
)
|
||||
continue;
|
||||
}
|
||||
if (1 === exitStatus) {
|
||||
prepareFreshStack(root, 0);
|
||||
@@ -10791,7 +10796,7 @@ var roots = new Map(),
|
||||
devToolsConfig$jscomp$inline_1168 = {
|
||||
findFiberByHostInstance: getInstanceFromTag,
|
||||
bundleType: 0,
|
||||
version: "19.0.0-canary-da7bd93e",
|
||||
version: "19.0.0-canary-fd8b4cb6",
|
||||
rendererPackageName: "react-native-renderer",
|
||||
rendererConfig: {
|
||||
getInspectorDataForInstance: getInspectorDataForInstance,
|
||||
@@ -10834,7 +10839,7 @@ var internals$jscomp$inline_1452 = {
|
||||
scheduleRoot: null,
|
||||
setRefreshHandler: null,
|
||||
getCurrentFiber: null,
|
||||
reconcilerVersion: "19.0.0-canary-da7bd93e"
|
||||
reconcilerVersion: "19.0.0-canary-fd8b4cb6"
|
||||
};
|
||||
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
|
||||
var hook$jscomp$inline_1453 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
|
||||
+10
-5
@@ -7,7 +7,7 @@
|
||||
* @noflow
|
||||
* @nolint
|
||||
* @preventMunge
|
||||
* @generated SignedSource<<fd734202c65fd13f05652a451948b4df>>
|
||||
* @generated SignedSource<<9a1a6e70657fd7e1a2634cc973de0331>>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -9798,13 +9798,18 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
|
||||
root,
|
||||
renderWasConcurrent
|
||||
);
|
||||
0 !== errorRetryLanes &&
|
||||
if (
|
||||
0 !== errorRetryLanes &&
|
||||
((lanes = errorRetryLanes),
|
||||
(exitStatus = recoverFromConcurrentError(
|
||||
root,
|
||||
renderWasConcurrent,
|
||||
errorRetryLanes
|
||||
)));
|
||||
)),
|
||||
(renderWasConcurrent = !1),
|
||||
2 !== exitStatus)
|
||||
)
|
||||
continue;
|
||||
}
|
||||
if (1 === exitStatus) {
|
||||
prepareFreshStack(root, 0);
|
||||
@@ -11497,7 +11502,7 @@ var roots = new Map(),
|
||||
devToolsConfig$jscomp$inline_1248 = {
|
||||
findFiberByHostInstance: getInstanceFromTag,
|
||||
bundleType: 0,
|
||||
version: "19.0.0-canary-2571bd6c",
|
||||
version: "19.0.0-canary-e1908119",
|
||||
rendererPackageName: "react-native-renderer",
|
||||
rendererConfig: {
|
||||
getInspectorDataForInstance: getInspectorDataForInstance,
|
||||
@@ -11553,7 +11558,7 @@ var roots = new Map(),
|
||||
scheduleRoot: null,
|
||||
setRefreshHandler: null,
|
||||
getCurrentFiber: null,
|
||||
reconcilerVersion: "19.0.0-canary-2571bd6c"
|
||||
reconcilerVersion: "19.0.0-canary-e1908119"
|
||||
});
|
||||
exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
|
||||
computeComponentStackForErrorReporting: function (reactTag) {
|
||||
|
||||
Reference in New Issue
Block a user