Skip to content

Commit d2b1651

Browse files
committed
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 3f9e237.
1 parent d0f9214 commit d2b1651

File tree

10 files changed

+112
-49
lines changed

10 files changed

+112
-49
lines changed

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-dev.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<fdd11cfd0a9f56383a8f5fff2c50fc9e>>
10+
* @generated SignedSource<<881e78352310ebeea1f473a89441e0c9>>
1111
*/
1212

1313
"use strict";
@@ -22773,20 +22773,31 @@ if (__DEV__) {
2277322773
} // Check if something threw
2277422774

2277522775
if (exitStatus === RootErrored) {
22776-
var originallyAttemptedLanes = lanes;
22776+
var lanesThatJustErrored = lanes;
2277722777
var errorRetryLanes = getLanesToRetrySynchronouslyOnError(
2277822778
root,
22779-
originallyAttemptedLanes
22779+
lanesThatJustErrored
2278022780
);
2278122781

2278222782
if (errorRetryLanes !== NoLanes) {
2278322783
lanes = errorRetryLanes;
2278422784
exitStatus = recoverFromConcurrentError(
2278522785
root,
22786-
originallyAttemptedLanes,
22786+
lanesThatJustErrored,
2278722787
errorRetryLanes
2278822788
);
22789-
renderWasConcurrent = false;
22789+
renderWasConcurrent = false; // Need to check the exit status again.
22790+
22791+
if (exitStatus !== RootErrored) {
22792+
// The root did not error this time. Restart the exit algorithm
22793+
// from the beginning.
22794+
// TODO: Refactor the exit algorithm to be less confusing. Maybe
22795+
// more branches + recursion instead of a loop. I think the only
22796+
// thing that causes it to be a loop is the RootDidNotComplete
22797+
// check. If that's true, then we don't need a loop/recursion
22798+
// at all.
22799+
continue;
22800+
}
2279022801
}
2279122802
}
2279222803

@@ -26590,7 +26601,7 @@ if (__DEV__) {
2659026601
return root;
2659126602
}
2659226603

26593-
var ReactVersion = "19.0.0-canary-27752dcf";
26604+
var ReactVersion = "19.0.0-canary-89546c49";
2659426605

2659526606
/*
2659626607
* The `'' + value` pattern (used in perf-sensitive code) throws for Symbol

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-prod.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<761db320e8f45126e2feb86ac8bca704>>
10+
* @generated SignedSource<<5159750d4ca65c393d679d370a71d7cb>>
1111
*/
1212

1313
"use strict";
@@ -7472,13 +7472,18 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
74727472
root,
74737473
renderWasConcurrent
74747474
);
7475-
0 !== errorRetryLanes &&
7475+
if (
7476+
0 !== errorRetryLanes &&
74767477
((lanes = errorRetryLanes),
74777478
(exitStatus = recoverFromConcurrentError(
74787479
root,
74797480
renderWasConcurrent,
74807481
errorRetryLanes
7481-
)));
7482+
)),
7483+
(renderWasConcurrent = !1),
7484+
2 !== exitStatus)
7485+
)
7486+
continue;
74827487
}
74837488
if (1 === exitStatus) {
74847489
prepareFreshStack(root, 0);
@@ -9119,7 +9124,7 @@ var devToolsConfig$jscomp$inline_1019 = {
91199124
throw Error("TestRenderer does not support findFiberByHostInstance()");
91209125
},
91219126
bundleType: 0,
9122-
version: "19.0.0-canary-3d34fdc4",
9127+
version: "19.0.0-canary-df9de31a",
91239128
rendererPackageName: "react-test-renderer"
91249129
};
91259130
var internals$jscomp$inline_1238 = {
@@ -9150,7 +9155,7 @@ var internals$jscomp$inline_1238 = {
91509155
scheduleRoot: null,
91519156
setRefreshHandler: null,
91529157
getCurrentFiber: null,
9153-
reconcilerVersion: "19.0.0-canary-3d34fdc4"
9158+
reconcilerVersion: "19.0.0-canary-df9de31a"
91549159
};
91559160
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
91569161
var hook$jscomp$inline_1239 = __REACT_DEVTOOLS_GLOBAL_HOOK__;

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-profiling.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<ec92ba0d6674d69f102b6b07bcc607e9>>
10+
* @generated SignedSource<<21f520424802b9edb8fc0a8c96178e1c>>
1111
*/
1212

1313
"use strict";
@@ -7966,13 +7966,18 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
79667966
root,
79677967
renderWasConcurrent
79687968
);
7969-
0 !== errorRetryLanes &&
7969+
if (
7970+
0 !== errorRetryLanes &&
79707971
((lanes = errorRetryLanes),
79717972
(exitStatus = recoverFromConcurrentError(
79727973
root,
79737974
renderWasConcurrent,
79747975
errorRetryLanes
7975-
)));
7976+
)),
7977+
(renderWasConcurrent = !1),
7978+
2 !== exitStatus)
7979+
)
7980+
continue;
79767981
}
79777982
if (1 === exitStatus) {
79787983
prepareFreshStack(root, 0);
@@ -9757,7 +9762,7 @@ var devToolsConfig$jscomp$inline_1082 = {
97579762
throw Error("TestRenderer does not support findFiberByHostInstance()");
97589763
},
97599764
bundleType: 0,
9760-
version: "19.0.0-canary-09ccb82c",
9765+
version: "19.0.0-canary-12ee58d1",
97619766
rendererPackageName: "react-test-renderer"
97629767
};
97639768
(function (internals) {
@@ -9801,7 +9806,7 @@ var devToolsConfig$jscomp$inline_1082 = {
98019806
scheduleRoot: null,
98029807
setRefreshHandler: null,
98039808
getCurrentFiber: null,
9804-
reconcilerVersion: "19.0.0-canary-09ccb82c"
9809+
reconcilerVersion: "19.0.0-canary-12ee58d1"
98059810
});
98069811
exports._Scheduler = Scheduler;
98079812
exports.act = act;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
64c8d2d45d49dbb2f59ea23e5e739eb79e124abc
1+
3f9e237a2feb74f1fca23b76d9d2e9e1713e2ba1

compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-dev.fb.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<494d27c89fdf2fb47e01cb552bf35a07>>
10+
* @generated SignedSource<<a4571cdfd524cd72b685d2a3adeafd2c>>
1111
*/
1212

1313
"use strict";
@@ -26242,20 +26242,31 @@ to return true:wantsResponderID| |
2624226242
} // Check if something threw
2624326243

2624426244
if (exitStatus === RootErrored) {
26245-
var originallyAttemptedLanes = lanes;
26245+
var lanesThatJustErrored = lanes;
2624626246
var errorRetryLanes = getLanesToRetrySynchronouslyOnError(
2624726247
root,
26248-
originallyAttemptedLanes
26248+
lanesThatJustErrored
2624926249
);
2625026250

2625126251
if (errorRetryLanes !== NoLanes) {
2625226252
lanes = errorRetryLanes;
2625326253
exitStatus = recoverFromConcurrentError(
2625426254
root,
26255-
originallyAttemptedLanes,
26255+
lanesThatJustErrored,
2625626256
errorRetryLanes
2625726257
);
26258-
renderWasConcurrent = false;
26258+
renderWasConcurrent = false; // Need to check the exit status again.
26259+
26260+
if (exitStatus !== RootErrored) {
26261+
// The root did not error this time. Restart the exit algorithm
26262+
// from the beginning.
26263+
// TODO: Refactor the exit algorithm to be less confusing. Maybe
26264+
// more branches + recursion instead of a loop. I think the only
26265+
// thing that causes it to be a loop is the RootDidNotComplete
26266+
// check. If that's true, then we don't need a loop/recursion
26267+
// at all.
26268+
continue;
26269+
}
2625926270
}
2626026271
}
2626126272

@@ -30237,7 +30248,7 @@ to return true:wantsResponderID| |
3023730248
return root;
3023830249
}
3023930250

30240-
var ReactVersion = "19.0.0-canary-d3b8f5e2";
30251+
var ReactVersion = "19.0.0-canary-704f6d1b";
3024130252

3024230253
/*
3024330254
* The `'' + value` pattern (used in perf-sensitive code) throws for Symbol

compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-prod.fb.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<16efbcb0da2cbd078a40efb1a0552fe8>>
10+
* @generated SignedSource<<2e33e857cb3eee98d5e57d1509811f8c>>
1111
*/
1212

1313
"use strict";
@@ -9055,13 +9055,18 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
90559055
root,
90569056
renderWasConcurrent
90579057
);
9058-
0 !== errorRetryLanes &&
9058+
if (
9059+
0 !== errorRetryLanes &&
90599060
((lanes = errorRetryLanes),
90609061
(exitStatus = recoverFromConcurrentError(
90619062
root,
90629063
renderWasConcurrent,
90639064
errorRetryLanes
9064-
)));
9065+
)),
9066+
(renderWasConcurrent = !1),
9067+
2 !== exitStatus)
9068+
)
9069+
continue;
90659070
}
90669071
if (1 === exitStatus) {
90679072
prepareFreshStack(root, 0);
@@ -10572,7 +10577,7 @@ var roots = new Map(),
1057210577
devToolsConfig$jscomp$inline_1099 = {
1057310578
findFiberByHostInstance: getInstanceFromNode,
1057410579
bundleType: 0,
10575-
version: "19.0.0-canary-120922b5",
10580+
version: "19.0.0-canary-a19c8da4",
1057610581
rendererPackageName: "react-native-renderer",
1057710582
rendererConfig: {
1057810583
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -10615,7 +10620,7 @@ var internals$jscomp$inline_1366 = {
1061510620
scheduleRoot: null,
1061610621
setRefreshHandler: null,
1061710622
getCurrentFiber: null,
10618-
reconcilerVersion: "19.0.0-canary-120922b5"
10623+
reconcilerVersion: "19.0.0-canary-a19c8da4"
1061910624
};
1062010625
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
1062110626
var hook$jscomp$inline_1367 = __REACT_DEVTOOLS_GLOBAL_HOOK__;

compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-profiling.fb.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<a3c25968a30f5f8cd268a3314ac130fe>>
10+
* @generated SignedSource<<5fa81808919e6dc188f7876d0baea787>>
1111
*/
1212

1313
"use strict";
@@ -9585,13 +9585,18 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
95859585
root,
95869586
renderWasConcurrent
95879587
);
9588-
0 !== errorRetryLanes &&
9588+
if (
9589+
0 !== errorRetryLanes &&
95899590
((lanes = errorRetryLanes),
95909591
(exitStatus = recoverFromConcurrentError(
95919592
root,
95929593
renderWasConcurrent,
95939594
errorRetryLanes
9594-
)));
9595+
)),
9596+
(renderWasConcurrent = !1),
9597+
2 !== exitStatus)
9598+
)
9599+
continue;
95959600
}
95969601
if (1 === exitStatus) {
95979602
prepareFreshStack(root, 0);
@@ -11277,7 +11282,7 @@ var roots = new Map(),
1127711282
devToolsConfig$jscomp$inline_1179 = {
1127811283
findFiberByHostInstance: getInstanceFromNode,
1127911284
bundleType: 0,
11280-
version: "19.0.0-canary-7ffb2ad4",
11285+
version: "19.0.0-canary-0d80c3d3",
1128111286
rendererPackageName: "react-native-renderer",
1128211287
rendererConfig: {
1128311288
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -11333,7 +11338,7 @@ var roots = new Map(),
1133311338
scheduleRoot: null,
1133411339
setRefreshHandler: null,
1133511340
getCurrentFiber: null,
11336-
reconcilerVersion: "19.0.0-canary-7ffb2ad4"
11341+
reconcilerVersion: "19.0.0-canary-0d80c3d3"
1133711342
});
1133811343
exports.createPortal = function (children, containerTag) {
1133911344
return createPortal$1(

compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactNativeRenderer-dev.fb.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<24fee48abc3bddda74443b28d17bbc3e>>
10+
* @generated SignedSource<<a64d4ff9138061f9c517e2327f49b803>>
1111
*/
1212

1313
"use strict";
@@ -26682,20 +26682,31 @@ to return true:wantsResponderID| |
2668226682
} // Check if something threw
2668326683

2668426684
if (exitStatus === RootErrored) {
26685-
var originallyAttemptedLanes = lanes;
26685+
var lanesThatJustErrored = lanes;
2668626686
var errorRetryLanes = getLanesToRetrySynchronouslyOnError(
2668726687
root,
26688-
originallyAttemptedLanes
26688+
lanesThatJustErrored
2668926689
);
2669026690

2669126691
if (errorRetryLanes !== NoLanes) {
2669226692
lanes = errorRetryLanes;
2669326693
exitStatus = recoverFromConcurrentError(
2669426694
root,
26695-
originallyAttemptedLanes,
26695+
lanesThatJustErrored,
2669626696
errorRetryLanes
2669726697
);
26698-
renderWasConcurrent = false;
26698+
renderWasConcurrent = false; // Need to check the exit status again.
26699+
26700+
if (exitStatus !== RootErrored) {
26701+
// The root did not error this time. Restart the exit algorithm
26702+
// from the beginning.
26703+
// TODO: Refactor the exit algorithm to be less confusing. Maybe
26704+
// more branches + recursion instead of a loop. I think the only
26705+
// thing that causes it to be a loop is the RootDidNotComplete
26706+
// check. If that's true, then we don't need a loop/recursion
26707+
// at all.
26708+
continue;
26709+
}
2669926710
}
2670026711
}
2670126712

@@ -30677,7 +30688,7 @@ to return true:wantsResponderID| |
3067730688
return root;
3067830689
}
3067930690

30680-
var ReactVersion = "19.0.0-canary-ae39878d";
30691+
var ReactVersion = "19.0.0-canary-f860c0a2";
3068130692

3068230693
/*
3068330694
* The `'' + value` pattern (used in perf-sensitive code) throws for Symbol

0 commit comments

Comments
 (0)