Skip to content

Commit fe026dd

Browse files
committed
useActionState: On error, cancel remaining actions (#29695)
Based on - #29694 --- If an action in the useActionState queue errors, we shouldn't run any subsequent actions. The contract of useActionState is that the actions run in sequence, and that one action can assume that all previous actions have completed successfully. For example, in a shopping cart UI, you might dispatch an "Add to cart" action followed by a "Checkout" action. If the "Add to cart" action errors, the "Checkout" action should not run. An implication of this change is that once useActionState falls into an error state, the only way to recover is to reset the component tree, i.e. by unmounting and remounting. The way to customize the error handling behavior is to wrap the action body in a try/catch. DiffTrain build for commit 9598c41.
1 parent bf759bd commit fe026dd

File tree

14 files changed

+321
-291
lines changed

14 files changed

+321
-291
lines changed

compiled-rn/VERSION_NATIVE_FB

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
19.0.0-native-fb-67b05be0d2-20240603
1+
19.0.0-native-fb-9598c41a20-20240603

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

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

1313
'use strict';
@@ -8612,9 +8612,16 @@ function dispatchActionState(fiber, actionQueue, setPendingState, setState, payl
86128612
throw new Error('Cannot update form state while rendering.');
86138613
}
86148614

8615+
var currentAction = actionQueue.action;
8616+
8617+
if (currentAction === null) {
8618+
// An earlier action errored. Subsequent actions should not run.
8619+
return;
8620+
}
8621+
86158622
var actionNode = {
86168623
payload: payload,
8617-
action: actionQueue.action,
8624+
action: currentAction,
86188625
next: null,
86198626
// circular
86208627
isTransition: true,
@@ -8773,28 +8780,23 @@ function onActionSuccess(actionQueue, actionNode, nextState) {
87738780
}
87748781

87758782
function onActionError(actionQueue, actionNode, error) {
8776-
actionNode.status = 'rejected';
8777-
actionNode.reason = error;
8778-
notifyActionListeners(actionNode); // Pop the action from the queue and run the next pending action, if there
8779-
// are any.
8780-
// TODO: We should instead abort all the remaining actions in the queue.
8781-
8783+
// Mark all the following actions as rejected.
87828784
var last = actionQueue.pending;
8785+
actionQueue.pending = null;
87838786

87848787
if (last !== null) {
87858788
var first = last.next;
87868789

8787-
if (first === last) {
8788-
// This was the last action in the queue.
8789-
actionQueue.pending = null;
8790-
} else {
8791-
// Remove the first node from the circular queue.
8792-
var next = first.next;
8793-
last.next = next; // Run the next action.
8790+
do {
8791+
actionNode.status = 'rejected';
8792+
actionNode.reason = error;
8793+
notifyActionListeners(actionNode);
8794+
actionNode = actionNode.next;
8795+
} while (actionNode !== first);
8796+
} // Prevent subsequent actions from being dispatched.
87948797

8795-
runActionStateAction(actionQueue, next);
8796-
}
8797-
}
8798+
8799+
actionQueue.action = null;
87988800
}
87998801

88008802
function notifyActionListeners(actionNode) {
@@ -23568,7 +23570,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition
2356823570
return root;
2356923571
}
2357023572

23571-
var ReactVersion = '19.0.0-rc-67b05be0d2-20240603';
23573+
var ReactVersion = '19.0.0-rc-9598c41a20-20240603';
2357223574

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

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

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<8797f6b701596391f0ce6aaf8dbd0345>>
10+
* @generated SignedSource<<c423cc156ab6bd82b7b2904bf63b4563>>
1111
*/
1212

1313
"use strict";
@@ -2776,29 +2776,32 @@ function dispatchActionState(
27762776
) {
27772777
if (isRenderPhaseUpdate(fiber))
27782778
throw Error("Cannot update form state while rendering.");
2779-
var actionNode = {
2780-
payload: payload,
2781-
action: actionQueue.action,
2782-
next: null,
2783-
isTransition: !0,
2784-
status: "pending",
2785-
value: null,
2786-
reason: null,
2787-
listeners: [],
2788-
then: function (listener) {
2789-
actionNode.listeners.push(listener);
2790-
}
2791-
};
2792-
null !== ReactSharedInternals.T
2793-
? setPendingState(!0)
2794-
: (actionNode.isTransition = !1);
2795-
setState(actionNode);
2796-
fiber = actionQueue.pending;
2797-
null === fiber
2798-
? ((actionNode.next = actionQueue.pending = actionNode),
2799-
runActionStateAction(actionQueue, actionNode))
2800-
: ((actionNode.next = fiber.next),
2801-
(actionQueue.pending = fiber.next = actionNode));
2779+
fiber = actionQueue.action;
2780+
if (null !== fiber) {
2781+
var actionNode = {
2782+
payload: payload,
2783+
action: fiber,
2784+
next: null,
2785+
isTransition: !0,
2786+
status: "pending",
2787+
value: null,
2788+
reason: null,
2789+
listeners: [],
2790+
then: function (listener) {
2791+
actionNode.listeners.push(listener);
2792+
}
2793+
};
2794+
null !== ReactSharedInternals.T
2795+
? setPendingState(!0)
2796+
: (actionNode.isTransition = !1);
2797+
setState(actionNode);
2798+
setPendingState = actionQueue.pending;
2799+
null === setPendingState
2800+
? ((actionNode.next = actionQueue.pending = actionNode),
2801+
runActionStateAction(actionQueue, actionNode))
2802+
: ((actionNode.next = setPendingState.next),
2803+
(actionQueue.pending = setPendingState.next = actionNode));
2804+
}
28022805
}
28032806
function runActionStateAction(actionQueue, node) {
28042807
var action = node.action,
@@ -2856,17 +2859,18 @@ function onActionSuccess(actionQueue, actionNode, nextState) {
28562859
runActionStateAction(actionQueue, nextState)));
28572860
}
28582861
function onActionError(actionQueue, actionNode, error) {
2859-
actionNode.status = "rejected";
2860-
actionNode.reason = error;
2861-
notifyActionListeners(actionNode);
2862-
actionNode = actionQueue.pending;
2863-
null !== actionNode &&
2864-
((error = actionNode.next),
2865-
error === actionNode
2866-
? (actionQueue.pending = null)
2867-
: ((error = error.next),
2868-
(actionNode.next = error),
2869-
runActionStateAction(actionQueue, error)));
2862+
var last = actionQueue.pending;
2863+
actionQueue.pending = null;
2864+
if (null !== last) {
2865+
last = last.next;
2866+
do
2867+
(actionNode.status = "rejected"),
2868+
(actionNode.reason = error),
2869+
notifyActionListeners(actionNode),
2870+
(actionNode = actionNode.next);
2871+
while (actionNode !== last);
2872+
}
2873+
actionQueue.action = null;
28702874
}
28712875
function notifyActionListeners(actionNode) {
28722876
actionNode = actionNode.listeners;
@@ -9332,7 +9336,7 @@ var devToolsConfig$jscomp$inline_1048 = {
93329336
throw Error("TestRenderer does not support findFiberByHostInstance()");
93339337
},
93349338
bundleType: 0,
9335-
version: "19.0.0-rc-67b05be0d2-20240603",
9339+
version: "19.0.0-rc-9598c41a20-20240603",
93369340
rendererPackageName: "react-test-renderer"
93379341
};
93389342
var internals$jscomp$inline_1235 = {
@@ -9363,7 +9367,7 @@ var internals$jscomp$inline_1235 = {
93639367
scheduleRoot: null,
93649368
setRefreshHandler: null,
93659369
getCurrentFiber: null,
9366-
reconcilerVersion: "19.0.0-rc-67b05be0d2-20240603"
9370+
reconcilerVersion: "19.0.0-rc-9598c41a20-20240603"
93679371
};
93689372
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
93699373
var hook$jscomp$inline_1236 = __REACT_DEVTOOLS_GLOBAL_HOOK__;

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

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<cc0f058c6b28d07a50a2d230bced40e3>>
10+
* @generated SignedSource<<529a2966d841e9b9340a413ab80ac499>>
1111
*/
1212

1313
"use strict";
@@ -2864,29 +2864,32 @@ function dispatchActionState(
28642864
) {
28652865
if (isRenderPhaseUpdate(fiber))
28662866
throw Error("Cannot update form state while rendering.");
2867-
var actionNode = {
2868-
payload: payload,
2869-
action: actionQueue.action,
2870-
next: null,
2871-
isTransition: !0,
2872-
status: "pending",
2873-
value: null,
2874-
reason: null,
2875-
listeners: [],
2876-
then: function (listener) {
2877-
actionNode.listeners.push(listener);
2878-
}
2879-
};
2880-
null !== ReactSharedInternals.T
2881-
? setPendingState(!0)
2882-
: (actionNode.isTransition = !1);
2883-
setState(actionNode);
2884-
fiber = actionQueue.pending;
2885-
null === fiber
2886-
? ((actionNode.next = actionQueue.pending = actionNode),
2887-
runActionStateAction(actionQueue, actionNode))
2888-
: ((actionNode.next = fiber.next),
2889-
(actionQueue.pending = fiber.next = actionNode));
2867+
fiber = actionQueue.action;
2868+
if (null !== fiber) {
2869+
var actionNode = {
2870+
payload: payload,
2871+
action: fiber,
2872+
next: null,
2873+
isTransition: !0,
2874+
status: "pending",
2875+
value: null,
2876+
reason: null,
2877+
listeners: [],
2878+
then: function (listener) {
2879+
actionNode.listeners.push(listener);
2880+
}
2881+
};
2882+
null !== ReactSharedInternals.T
2883+
? setPendingState(!0)
2884+
: (actionNode.isTransition = !1);
2885+
setState(actionNode);
2886+
setPendingState = actionQueue.pending;
2887+
null === setPendingState
2888+
? ((actionNode.next = actionQueue.pending = actionNode),
2889+
runActionStateAction(actionQueue, actionNode))
2890+
: ((actionNode.next = setPendingState.next),
2891+
(actionQueue.pending = setPendingState.next = actionNode));
2892+
}
28902893
}
28912894
function runActionStateAction(actionQueue, node) {
28922895
var action = node.action,
@@ -2944,17 +2947,18 @@ function onActionSuccess(actionQueue, actionNode, nextState) {
29442947
runActionStateAction(actionQueue, nextState)));
29452948
}
29462949
function onActionError(actionQueue, actionNode, error) {
2947-
actionNode.status = "rejected";
2948-
actionNode.reason = error;
2949-
notifyActionListeners(actionNode);
2950-
actionNode = actionQueue.pending;
2951-
null !== actionNode &&
2952-
((error = actionNode.next),
2953-
error === actionNode
2954-
? (actionQueue.pending = null)
2955-
: ((error = error.next),
2956-
(actionNode.next = error),
2957-
runActionStateAction(actionQueue, error)));
2950+
var last = actionQueue.pending;
2951+
actionQueue.pending = null;
2952+
if (null !== last) {
2953+
last = last.next;
2954+
do
2955+
(actionNode.status = "rejected"),
2956+
(actionNode.reason = error),
2957+
notifyActionListeners(actionNode),
2958+
(actionNode = actionNode.next);
2959+
while (actionNode !== last);
2960+
}
2961+
actionQueue.action = null;
29582962
}
29592963
function notifyActionListeners(actionNode) {
29602964
actionNode = actionNode.listeners;
@@ -9954,7 +9958,7 @@ var devToolsConfig$jscomp$inline_1131 = {
99549958
throw Error("TestRenderer does not support findFiberByHostInstance()");
99559959
},
99569960
bundleType: 0,
9957-
version: "19.0.0-rc-67b05be0d2-20240603",
9961+
version: "19.0.0-rc-9598c41a20-20240603",
99589962
rendererPackageName: "react-test-renderer"
99599963
};
99609964
(function (internals) {
@@ -9998,7 +10002,7 @@ var devToolsConfig$jscomp$inline_1131 = {
999810002
scheduleRoot: null,
999910003
setRefreshHandler: null,
1000010004
getCurrentFiber: null,
10001-
reconcilerVersion: "19.0.0-rc-67b05be0d2-20240603"
10005+
reconcilerVersion: "19.0.0-rc-9598c41a20-20240603"
1000210006
});
1000310007
exports._Scheduler = Scheduler;
1000410008
exports.act = act;

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/react/cjs/React-dev.js

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

1313
'use strict';
@@ -24,7 +24,7 @@ if (
2424
}
2525
var dynamicFlagsUntyped = require('ReactNativeInternalFeatureFlags');
2626

27-
var ReactVersion = '19.0.0-rc-67b05be0d2-20240603';
27+
var ReactVersion = '19.0.0-rc-9598c41a20-20240603';
2828

2929
// Re-export dynamic flags from the internal module.
3030
var dynamicFlags = dynamicFlagsUntyped; // We destructure each value before re-exporting to avoid a dynamic look-up on

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/react/cjs/React-prod.js

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

1313
"use strict";
@@ -604,4 +604,4 @@ exports.useSyncExternalStore = function (
604604
exports.useTransition = function () {
605605
return ReactSharedInternals.H.useTransition();
606606
};
607-
exports.version = "19.0.0-rc-67b05be0d2-20240603";
607+
exports.version = "19.0.0-rc-9598c41a20-20240603";

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/react/cjs/React-profiling.js

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

1313
"use strict";
@@ -608,7 +608,7 @@ exports.useSyncExternalStore = function (
608608
exports.useTransition = function () {
609609
return ReactSharedInternals.H.useTransition();
610610
};
611-
exports.version = "19.0.0-rc-67b05be0d2-20240603";
611+
exports.version = "19.0.0-rc-9598c41a20-20240603";
612612
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
613613
"function" ===
614614
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
67b05be0d216c4efebc4bb5acb12c861a18bd87c
1+
9598c41a20162c8a9d57ccf6a356aa183b00b61a

0 commit comments

Comments
 (0)