From 0ddb2297da3de25df9688f11cd8e4e89e3a2747f Mon Sep 17 00:00:00 2001 From: acdlite Date: Fri, 21 Apr 2023 17:36:27 +0000 Subject: [PATCH] Rethrow errors from form actions (#26689) This is the next step toward full support for async form actions. Errors thrown inside form actions should cause the form to re-render and throw the error so it can be captured by an error boundary. The behavior is the same if the `
` had an internal useTransition hook, which is pretty much exactly how we implement it, too. The first time an action is called, the form's HostComponent is "upgraded" to become stateful, by lazily mounting a list of hooks. The rest of the implementation for function components can be shared. Because the error handling behavior added in this commit is just using useTransition under-the-hood, it also handles pending states, too. However, this pending state can't be observed until we add a new hook for that purpose. I'll add this next. DiffTrain build for commit https://github.com/facebook/react/commit/fd3fb8e3c5d1c977f4bfa73d715143804c69d4b0. --- .../cjs/ReactTestRenderer-dev.js | 14 ++++++++++++-- .../cjs/ReactTestRenderer-prod.js | 8 +++++--- .../cjs/ReactTestRenderer-profiling.js | 8 +++++--- .../js/RKJSModules/vendor/react/cjs/React-dev.js | 2 +- .../js/RKJSModules/vendor/react/cjs/React-prod.js | 2 +- .../vendor/react/cjs/React-profiling.js | 2 +- .../Libraries/Renderer/REVISION | 2 +- .../Renderer/implementations/ReactFabric-dev.fb.js | 14 ++++++++++++-- .../implementations/ReactFabric-prod.fb.js | 8 +++++--- .../implementations/ReactFabric-profiling.fb.js | 8 +++++--- .../implementations/ReactNativeRenderer-dev.fb.js | 14 ++++++++++++-- .../implementations/ReactNativeRenderer-prod.fb.js | 8 +++++--- .../ReactNativeRenderer-profiling.fb.js | 8 +++++--- 13 files changed, 70 insertions(+), 28 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 652c5b1a20..cefe2370e5 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<<968b9a9a87595c8009c33efb698508af>> */ 'use strict'; @@ -21437,6 +21437,16 @@ function replaySuspendedUnitOfWork(unitOfWork) { break; } + case HostComponent: { + // Some host components are stateful (that's how we implement form + // actions) but we don't bother to reuse the memoized state because it's + // not worth the extra code. The main reason to reuse the previous hooks + // is to reuse uncached promises, but we happen to know that the only + // promises that a host component might suspend on are definitely cached + // because they are controlled by us. So don't bother. + resetHooksOnUnwind(); // Fallthrough to the next branch. + } + default: { // Other types besides function components are reset completely before // being replayed. Currently this only happens when a Usable type is @@ -23838,7 +23848,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-next-c57a0f68a-20230421"; +var ReactVersion = "18.3.0-next-fd3fb8e3c-20230421"; // Might add PROFILE later. 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 eeb9b4efa1..a80177621d 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<<4f902d10ff6c4c49a6f2e765b4662a5e>> + * @generated SignedSource<<3dd5e105138064b59966612a35ec1f24>> */ "use strict"; @@ -6973,6 +6973,8 @@ function replaySuspendedUnitOfWork(unitOfWork) { workInProgressRootRenderLanes ); break; + case 5: + resetHooksOnUnwind(); default: unwindInterruptedWork(current, unitOfWork), (unitOfWork = workInProgress = @@ -8599,7 +8601,7 @@ var devToolsConfig$jscomp$inline_1021 = { throw Error("TestRenderer does not support findFiberByHostInstance()"); }, bundleType: 0, - version: "18.3.0-next-c57a0f68a-20230421", + version: "18.3.0-next-fd3fb8e3c-20230421", rendererPackageName: "react-test-renderer" }; var internals$jscomp$inline_1206 = { @@ -8630,7 +8632,7 @@ var internals$jscomp$inline_1206 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-c57a0f68a-20230421" + reconcilerVersion: "18.3.0-next-fd3fb8e3c-20230421" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1207 = __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 b4252d4453..bed5e94f0e 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<<7b0d73bfc531dfd6dc0711c8a343756d>> + * @generated SignedSource<<778f87e6da120be9b98db0d06274b299>> */ "use strict"; @@ -7324,6 +7324,8 @@ function replaySuspendedUnitOfWork(unitOfWork) { workInProgressRootRenderLanes ); break; + case 5: + resetHooksOnUnwind(); default: unwindInterruptedWork(current, unitOfWork), (unitOfWork = workInProgress = @@ -9025,7 +9027,7 @@ var devToolsConfig$jscomp$inline_1063 = { throw Error("TestRenderer does not support findFiberByHostInstance()"); }, bundleType: 0, - version: "18.3.0-next-c57a0f68a-20230421", + version: "18.3.0-next-fd3fb8e3c-20230421", rendererPackageName: "react-test-renderer" }; var internals$jscomp$inline_1247 = { @@ -9056,7 +9058,7 @@ var internals$jscomp$inline_1247 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-c57a0f68a-20230421" + reconcilerVersion: "18.3.0-next-fd3fb8e3c-20230421" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1248 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-dev.js b/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-dev.js index 3b7a8c3191..17f6aa131e 100644 --- a/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-dev.js +++ b/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-dev.js @@ -27,7 +27,7 @@ if ( } "use strict"; -var ReactVersion = "18.3.0-next-c57a0f68a-20230421"; +var ReactVersion = "18.3.0-next-fd3fb8e3c-20230421"; // ATTENTION // When adding new symbols to this file, diff --git a/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-prod.js b/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-prod.js index a46222b6e2..109c0e562a 100644 --- a/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-prod.js +++ b/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-prod.js @@ -639,4 +639,4 @@ exports.useSyncExternalStore = function ( ); }; exports.useTransition = useTransition; -exports.version = "18.3.0-next-c57a0f68a-20230421"; +exports.version = "18.3.0-next-fd3fb8e3c-20230421"; diff --git a/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-profiling.js b/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-profiling.js index de53dd8208..06a2add832 100644 --- a/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-profiling.js +++ b/compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-profiling.js @@ -642,7 +642,7 @@ exports.useSyncExternalStore = function ( ); }; exports.useTransition = useTransition; -exports.version = "18.3.0-next-c57a0f68a-20230421"; +exports.version = "18.3.0-next-fd3fb8e3c-20230421"; /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ if ( 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 d673caef2d..9f0329cd94 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 @@ -c57a0f68a49d0a7fbe45e7b28820478d0fa4e32f +fd3fb8e3c5d1c977f4bfa73d715143804c69d4b0 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 e7cf616e76..b9f0e6401f 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<<40e2743803d8d937ee642c7e74254e74>> + * @generated SignedSource<> */ 'use strict'; @@ -24609,6 +24609,16 @@ function replaySuspendedUnitOfWork(unitOfWork) { break; } + case HostComponent: { + // Some host components are stateful (that's how we implement form + // actions) but we don't bother to reuse the memoized state because it's + // not worth the extra code. The main reason to reuse the previous hooks + // is to reuse uncached promises, but we happen to know that the only + // promises that a host component might suspend on are definitely cached + // because they are controlled by us. So don't bother. + resetHooksOnUnwind(); // Fallthrough to the next branch. + } + default: { // Other types besides function components are reset completely before // being replayed. Currently this only happens when a Usable type is @@ -27148,7 +27158,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-next-c57a0f68a-20230421"; +var ReactVersion = "18.3.0-next-fd3fb8e3c-20230421"; function createPortal$1( children, 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 006ae7e34b..407cc56ae7 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<<472cb7f28d742af2e290a0816a880f33>> + * @generated SignedSource<<388ecd5d71ee2c3022a43487fedc6a5a>> */ "use strict"; @@ -8063,6 +8063,8 @@ function replaySuspendedUnitOfWork(unitOfWork) { workInProgressRootRenderLanes ); break; + case 5: + resetHooksOnUnwind(); default: unwindInterruptedWork(current, unitOfWork), (unitOfWork = workInProgress = @@ -9470,7 +9472,7 @@ var roots = new Map(), devToolsConfig$jscomp$inline_1045 = { findFiberByHostInstance: getInstanceFromNode, bundleType: 0, - version: "18.3.0-next-c57a0f68a-20230421", + version: "18.3.0-next-fd3fb8e3c-20230421", rendererPackageName: "react-native-renderer", rendererConfig: { getInspectorDataForViewTag: function () { @@ -9512,7 +9514,7 @@ var internals$jscomp$inline_1276 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-c57a0f68a-20230421" + reconcilerVersion: "18.3.0-next-fd3fb8e3c-20230421" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1277 = __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 02d4950f51..a04f77eb82 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<<8e8a7cd3892f3b9331677bf16ba6a24b>> */ @@ -8660,6 +8660,8 @@ function replaySuspendedUnitOfWork(unitOfWork) { workInProgressRootRenderLanes ); break; + case 5: + resetHooksOnUnwind(); default: unwindInterruptedWork(current, unitOfWork), (unitOfWork = workInProgress = @@ -10179,7 +10181,7 @@ var roots = new Map(), devToolsConfig$jscomp$inline_1123 = { findFiberByHostInstance: getInstanceFromNode, bundleType: 0, - version: "18.3.0-next-c57a0f68a-20230421", + version: "18.3.0-next-fd3fb8e3c-20230421", rendererPackageName: "react-native-renderer", rendererConfig: { getInspectorDataForViewTag: function () { @@ -10234,7 +10236,7 @@ var roots = new Map(), scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-c57a0f68a-20230421" + reconcilerVersion: "18.3.0-next-fd3fb8e3c-20230421" }); 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 d40bd6bea9..53fd9f866f 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<<36dfe2e6840704492b6fbf0d00f5b91a>> + * @generated SignedSource<<6a9f3cb903b49266953a4dde78ca03fb>> */ 'use strict'; @@ -25122,6 +25122,16 @@ function replaySuspendedUnitOfWork(unitOfWork) { break; } + case HostComponent: { + // Some host components are stateful (that's how we implement form + // actions) but we don't bother to reuse the memoized state because it's + // not worth the extra code. The main reason to reuse the previous hooks + // is to reuse uncached promises, but we happen to know that the only + // promises that a host component might suspend on are definitely cached + // because they are controlled by us. So don't bother. + resetHooksOnUnwind(); // Fallthrough to the next branch. + } + default: { // Other types besides function components are reset completely before // being replayed. Currently this only happens when a Usable type is @@ -27661,7 +27671,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-next-c57a0f68a-20230421"; +var ReactVersion = "18.3.0-next-fd3fb8e3c-20230421"; function createPortal$1( children, 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 212ff0af11..004187456e 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<<54d01cff9e9c78ffba08f73a6631a077>> + * @generated SignedSource<<04b313069e799ea7f158cb3281fe8365>> */ "use strict"; @@ -8315,6 +8315,8 @@ function replaySuspendedUnitOfWork(unitOfWork) { workInProgressRootRenderLanes ); break; + case 5: + resetHooksOnUnwind(); default: unwindInterruptedWork(current, unitOfWork), (unitOfWork = workInProgress = @@ -9729,7 +9731,7 @@ var roots = new Map(), devToolsConfig$jscomp$inline_1100 = { findFiberByHostInstance: getInstanceFromTag, bundleType: 0, - version: "18.3.0-next-c57a0f68a-20230421", + version: "18.3.0-next-fd3fb8e3c-20230421", rendererPackageName: "react-native-renderer", rendererConfig: { getInspectorDataForViewTag: function () { @@ -9771,7 +9773,7 @@ var internals$jscomp$inline_1345 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-c57a0f68a-20230421" + reconcilerVersion: "18.3.0-next-fd3fb8e3c-20230421" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1346 = __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 d37742fa97..52305bf86d 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<<1ac72a305be24eff51144b88fe9c565d>> + * @generated SignedSource<<9b14144dbccddb7d3273b95ff3f77a1c>> */ @@ -8912,6 +8912,8 @@ function replaySuspendedUnitOfWork(unitOfWork) { workInProgressRootRenderLanes ); break; + case 5: + resetHooksOnUnwind(); default: unwindInterruptedWork(current, unitOfWork), (unitOfWork = workInProgress = @@ -10438,7 +10440,7 @@ var roots = new Map(), devToolsConfig$jscomp$inline_1178 = { findFiberByHostInstance: getInstanceFromTag, bundleType: 0, - version: "18.3.0-next-c57a0f68a-20230421", + version: "18.3.0-next-fd3fb8e3c-20230421", rendererPackageName: "react-native-renderer", rendererConfig: { getInspectorDataForViewTag: function () { @@ -10493,7 +10495,7 @@ var roots = new Map(), scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-c57a0f68a-20230421" + reconcilerVersion: "18.3.0-next-fd3fb8e3c-20230421" }); exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = { computeComponentStackForErrorReporting: function (reactTag) {