From d2b16516a1aa74828a95dfa8a58f17e4e04352e9 Mon Sep 17 00:00:00 2001 From: acdlite Date: Tue, 9 Apr 2024 21:16:18 +0000 Subject: [PATCH] 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. --- .../cjs/ReactTestRenderer-dev.js | 23 ++++++++++++++----- .../cjs/ReactTestRenderer-prod.js | 15 ++++++++---- .../cjs/ReactTestRenderer-profiling.js | 15 ++++++++---- .../Libraries/Renderer/REVISION | 2 +- .../implementations/ReactFabric-dev.fb.js | 23 ++++++++++++++----- .../implementations/ReactFabric-prod.fb.js | 15 ++++++++---- .../ReactFabric-profiling.fb.js | 15 ++++++++---- .../ReactNativeRenderer-dev.fb.js | 23 ++++++++++++++----- .../ReactNativeRenderer-prod.fb.js | 15 ++++++++---- .../ReactNativeRenderer-profiling.fb.js | 15 ++++++++---- 10 files changed, 112 insertions(+), 49 deletions(-) diff --git a/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-dev.js b/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-dev.js index a5e3085a9feba..7ec6864e8de1b 100644 --- a/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-dev.js +++ b/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-dev.js @@ -7,7 +7,7 @@ * @noflow * @nolint * @preventMunge - * @generated SignedSource<> + * @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 diff --git a/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-prod.js b/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-prod.js index 4f7987f6e7aaa..e9e8cbacb1434 100644 --- a/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-prod.js +++ b/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-prod.js @@ -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__; diff --git a/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-profiling.js b/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-profiling.js index 8a4025c1d752f..10046c7616661 100644 --- a/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-profiling.js +++ b/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-profiling.js @@ -7,7 +7,7 @@ * @noflow * @nolint * @preventMunge - * @generated SignedSource<> + * @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; diff --git a/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/REVISION b/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/REVISION index 39a21b964d469..542f0b4ea6284 100644 --- a/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/REVISION +++ b/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/REVISION @@ -1 +1 @@ -64c8d2d45d49dbb2f59ea23e5e739eb79e124abc +3f9e237a2feb74f1fca23b76d9d2e9e1713e2ba1 diff --git a/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-dev.fb.js b/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-dev.fb.js index 1f035a37005ec..89c0330d7710c 100644 --- a/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-dev.fb.js +++ b/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-dev.fb.js @@ -7,7 +7,7 @@ * @noflow * @nolint * @preventMunge - * @generated SignedSource<<494d27c89fdf2fb47e01cb552bf35a07>> + * @generated SignedSource<> */ "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 diff --git a/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-prod.fb.js b/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-prod.fb.js index 3932f79d33d25..bf58a83d2b1c3 100644 --- a/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-prod.fb.js +++ b/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-prod.fb.js @@ -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__; diff --git a/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-profiling.fb.js b/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-profiling.fb.js index 566ba3b106cdc..87dee6e8caf30 100644 --- a/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-profiling.fb.js +++ b/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-profiling.fb.js @@ -7,7 +7,7 @@ * @noflow * @nolint * @preventMunge - * @generated SignedSource<> + * @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( diff --git a/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactNativeRenderer-dev.fb.js b/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactNativeRenderer-dev.fb.js index c4af076fe1dd0..b4a20dfb64103 100644 --- a/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactNativeRenderer-dev.fb.js +++ b/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactNativeRenderer-dev.fb.js @@ -7,7 +7,7 @@ * @noflow * @nolint * @preventMunge - * @generated SignedSource<<24fee48abc3bddda74443b28d17bbc3e>> + * @generated SignedSource<> */ "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 diff --git a/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactNativeRenderer-prod.fb.js b/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactNativeRenderer-prod.fb.js index 55306253f4612..da420c0ec4fc1 100644 --- a/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactNativeRenderer-prod.fb.js +++ b/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactNativeRenderer-prod.fb.js @@ -7,7 +7,7 @@ * @noflow * @nolint * @preventMunge - * @generated SignedSource<<9314995819fee277c04cd34d21bec2c4>> + * @generated SignedSource<> */ "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__; diff --git a/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.fb.js b/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.fb.js index f59c09c22bcf8..9fc1f5898a526 100644 --- a/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.fb.js +++ b/compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.fb.js @@ -7,7 +7,7 @@ * @noflow * @nolint * @preventMunge - * @generated SignedSource<> + * @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) {