Remove enableAsyncActions (#31757)
Based on https://github.com/facebook/react/pull/31756 This is landed everywhere
Ricky committed
Dec 13, 2024 at 13:58 UTC
ef63718a27407b6d6b262d6be92e6bf0a87ff1a3
27 files changed
+394
-685
packages/react-debug-tools/src/ReactDebugHooks.js
+4
-17
@@ -104,22 +104,14 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
104
);
105
Dispatcher.useDeferredValue(null);
106
Dispatcher.useMemo(() => null);
107
+ Dispatcher.useOptimistic(null, (s: mixed, a: mixed) => s);
108
+ Dispatcher.useFormState((s: mixed, p: mixed) => s, null);
109
+ Dispatcher.useActionState((s: mixed, p: mixed) => s, null);
110
+ Dispatcher.useHostTransitionStatus();
111
if (typeof Dispatcher.useMemoCache === 'function') {
112
// This type check is for Flow only.
113
Dispatcher.useMemoCache(0);
114
}
111
- if (typeof Dispatcher.useOptimistic === 'function') {
112
- // This type check is for Flow only.
113
- Dispatcher.useOptimistic(null, (s: mixed, a: mixed) => s);
114
- }
115
- if (typeof Dispatcher.useFormState === 'function') {
116
- // This type check is for Flow only.
117
- Dispatcher.useFormState((s: mixed, p: mixed) => s, null);
118
- }
119
- if (typeof Dispatcher.useActionState === 'function') {
120
- // This type check is for Flow only.
121
- Dispatcher.useActionState((s: mixed, p: mixed) => s, null);
122
- }
115
if (typeof Dispatcher.use === 'function') {
116
// This type check is for Flow only.
117
Dispatcher.use(
@@ -143,11 +135,6 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
135
}
136
137
Dispatcher.useId();
146
-
147
- if (typeof Dispatcher.useHostTransitionStatus === 'function') {
148
- // This type check is for Flow only.
149
- Dispatcher.useHostTransitionStatus();
150
- }
138
} finally {
139
readHookLog = hookLog;
140
hookLog = [];
packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js
-2
@@ -2581,7 +2581,6 @@ describe('ReactHooksInspectionIntegration', () => {
2581
`);
2582
});
2583
2584
- // @gate enableAsyncActions
2584
it('should support useOptimistic hook', async () => {
2585
const useOptimistic = React.useOptimistic;
2586
function Foo() {
@@ -2647,7 +2646,6 @@ describe('ReactHooksInspectionIntegration', () => {
2646
`);
2647
});
2648
2650
- // @gate enableAsyncActions
2649
it('should support useActionState hook', async () => {
2650
function Foo() {
2651
const [value] = React.useActionState(function increment(n) {
packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
+2
-4
@@ -91,7 +91,6 @@ import {
91
enableCreateEventHandleAPI,
92
enableScopeAPI,
93
enableTrustedTypesIntegration,
94
- enableAsyncActions,
94
disableLegacyMode,
95
enableMoveBefore,
96
} from 'shared/ReactFeatureFlags';
@@ -1378,9 +1377,8 @@ function getNextHydratable(node: ?Node) {
1377
nodeData === SUSPENSE_START_DATA ||
1378
nodeData === SUSPENSE_FALLBACK_START_DATA ||
1379
nodeData === SUSPENSE_PENDING_START_DATA ||
1381
- (enableAsyncActions &&
1382
- (nodeData === FORM_STATE_IS_MATCHING ||
1383
- nodeData === FORM_STATE_IS_NOT_MATCHING))
1380
+ nodeData === FORM_STATE_IS_MATCHING ||
1381
+ nodeData === FORM_STATE_IS_NOT_MATCHING
1382
) {
1383
break;
1384
}
packages/react-dom-bindings/src/shared/ReactDOMFormActions.js
+4
-15
@@ -10,7 +10,6 @@
10
import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
11
import type {Awaited} from 'shared/ReactTypes';
12
13
-import {enableAsyncActions} from 'shared/ReactFeatureFlags';
13
import ReactSharedInternals from 'shared/ReactSharedInternals';
14
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
15
@@ -66,13 +65,8 @@ function resolveDispatcher() {
65
}
66
67
export function useFormStatus(): FormStatus {
69
- if (!enableAsyncActions) {
70
- throw new Error('Not implemented.');
71
- } else {
72
- const dispatcher = resolveDispatcher();
73
- // $FlowFixMe[not-a-function] We know this exists because of the feature check above.
74
- return dispatcher.useHostTransitionStatus();
75
- }
68
+ const dispatcher = resolveDispatcher();
69
+ return dispatcher.useHostTransitionStatus();
70
}
71
72
export function useFormState<S, P>(
@@ -80,13 +74,8 @@ export function useFormState<S, P>(
74
initialState: Awaited<S>,
75
permalink?: string,
76
): [Awaited<S>, (P) => void, boolean] {
83
- if (!enableAsyncActions) {
84
- throw new Error('Not implemented.');
85
- } else {
86
- const dispatcher = resolveDispatcher();
87
- // $FlowFixMe[not-a-function] This is unstable, thus optional
88
- return dispatcher.useFormState(action, initialState, permalink);
89
- }
77
+ const dispatcher = resolveDispatcher();
78
+ return dispatcher.useFormState(action, initialState, permalink);
79
}
80
81
export function requestFormReset(form: HTMLFormElement) {
packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js
-3
@@ -398,7 +398,6 @@ describe('ReactDOMFizzForm', () => {
398
expect(buttonRef.current.hasAttribute('formTarget')).toBe(false);
399
});
400
401
- // @gate enableAsyncActions
401
it('useFormStatus is not pending during server render', async () => {
402
function App() {
403
const {pending} = useFormStatus();
@@ -488,7 +487,6 @@ describe('ReactDOMFizzForm', () => {
487
expect(rootActionCalled).toBe(false);
488
});
489
491
- // @gate enableAsyncActions
490
it('useOptimistic returns passthrough value', async () => {
491
function App() {
492
const [optimisticState] = useOptimistic('hi');
@@ -507,7 +505,6 @@ describe('ReactDOMFizzForm', () => {
505
expect(container.textContent).toBe('hi');
506
});
507
510
- // @gate enableAsyncActions
508
it('useActionState returns initial state', async () => {
509
async function action(state) {
510
return state;
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
-2
@@ -6330,7 +6330,6 @@ describe('ReactDOMFizzServer', () => {
6330
expect(getVisibleChildren(container)).toEqual('Hi');
6331
});
6332
6333
- // @gate enableAsyncActions
6333
it('useActionState hydrates without a mismatch', async () => {
6334
// This is testing an implementation detail: useActionState emits comment
6335
// nodes into the SSR stream, so this checks that they are handled correctly
@@ -6383,7 +6382,6 @@ describe('ReactDOMFizzServer', () => {
6382
expect(childRef.current).toBe(child);
6383
});
6384
6386
- // @gate enableAsyncActions
6385
it("useActionState hydrates without a mismatch if there's a render phase update", async () => {
6386
async function action(state) {
6387
return state;
packages/react-dom/src/__tests__/ReactDOMForm-test.js
-25
@@ -669,7 +669,6 @@ describe('ReactDOMForm', () => {
669
expect(actionCalled).toBe(false);
670
});
671
672
- // @gate enableAsyncActions
672
it('form actions are transitions', async () => {
673
const formRef = React.createRef();
674
@@ -707,7 +706,6 @@ describe('ReactDOMForm', () => {
706
expect(container.textContent).toBe('Updated');
707
});
708
710
- // @gate enableAsyncActions
709
it('multiple form actions', async () => {
710
const formRef = React.createRef();
711
@@ -798,12 +796,6 @@ describe('ReactDOMForm', () => {
796
});
797
798
it('sync errors in form actions can be captured by an error boundary', async () => {
801
- if (gate(flags => !flags.enableAsyncActions)) {
802
- // TODO: Uncaught JSDOM errors fail the test after the scope has finished
803
- // so don't work with the `gate` mechanism.
804
- return;
805
- }
806
-
799
class ErrorBoundary extends React.Component {
800
state = {error: null};
801
static getDerivedStateFromError(error) {
@@ -844,12 +836,6 @@ describe('ReactDOMForm', () => {
836
});
837
838
it('async errors in form actions can be captured by an error boundary', async () => {
847
- if (gate(flags => !flags.enableAsyncActions)) {
848
- // TODO: Uncaught JSDOM errors fail the test after the scope has finished
849
- // so don't work with the `gate` mechanism.
850
- return;
851
- }
852
-
839
class ErrorBoundary extends React.Component {
840
state = {error: null};
841
static getDerivedStateFromError(error) {
@@ -895,7 +881,6 @@ describe('ReactDOMForm', () => {
881
expect(container.textContent).toBe('Oh no!');
882
});
883
898
- // @gate enableAsyncActions
884
it('useFormStatus reads the status of a pending form action', async () => {
885
const formRef = React.createRef();
886
@@ -992,7 +977,6 @@ describe('ReactDOMForm', () => {
977
);
978
});
979
995
- // @gate enableAsyncActions
980
it('useActionState updates state asynchronously and queues multiple actions', async () => {
981
let actionCounter = 0;
982
async function action(state, type) {
@@ -1052,7 +1036,6 @@ describe('ReactDOMForm', () => {
1036
expect(container.textContent).toBe('2');
1037
});
1038
1055
- // @gate enableAsyncActions
1039
it('useActionState supports inline actions', async () => {
1040
let increment;
1041
function App({stepSize}) {
@@ -1084,7 +1067,6 @@ describe('ReactDOMForm', () => {
1067
assertLog(['Pending 1', '11']);
1068
});
1069
1087
- // @gate enableAsyncActions
1070
it('useActionState: dispatch throws if called during render', async () => {
1071
function App() {
1072
const [state, dispatch, isPending] = useActionState(async () => {}, 0);
@@ -1100,7 +1082,6 @@ describe('ReactDOMForm', () => {
1082
});
1083
});
1084
1103
- // @gate enableAsyncActions
1085
it('useActionState: queues multiple actions and runs them in order', async () => {
1086
let action;
1087
function App() {
@@ -1132,7 +1113,6 @@ describe('ReactDOMForm', () => {
1113
expect(container.textContent).toBe('D');
1114
});
1115
1135
- // @gate enableAsyncActions
1116
it(
1117
'useActionState: when calling a queued action, uses the implementation ' +
1118
'that was current at the time it was dispatched, not the most recent one',
@@ -1179,7 +1159,6 @@ describe('ReactDOMForm', () => {
1159
},
1160
);
1161
1182
- // @gate enableAsyncActions
1162
it('useActionState: works if action is sync', async () => {
1163
let increment;
1164
function App({stepSize}) {
@@ -1211,7 +1190,6 @@ describe('ReactDOMForm', () => {
1190
assertLog(['Pending 1', '11']);
1191
});
1192
1214
- // @gate enableAsyncActions
1193
it('useActionState: can mix sync and async actions', async () => {
1194
let action;
1195
function App() {
@@ -1239,7 +1217,6 @@ describe('ReactDOMForm', () => {
1217
expect(container.textContent).toBe('E');
1218
});
1219
1242
- // @gate enableAsyncActions
1220
it('useActionState: error handling (sync action)', async () => {
1221
class ErrorBoundary extends React.Component {
1222
state = {error: null};
@@ -1288,7 +1265,6 @@ describe('ReactDOMForm', () => {
1265
expect(container.textContent).toBe('Caught an error: Oops!');
1266
});
1267
1291
- // @gate enableAsyncActions
1268
it('useActionState: error handling (async action)', async () => {
1269
class ErrorBoundary extends React.Component {
1270
state = {error: null};
@@ -1394,7 +1370,6 @@ describe('ReactDOMForm', () => {
1370
expect(container.textContent).toBe('Caught an error: Oops!');
1371
});
1372
1397
- // @gate enableAsyncActions
1373
it('useActionState works in StrictMode', async () => {
1374
let actionCounter = 0;
1375
async function action(state, type) {
packages/react-dom/src/client/ReactDOMRoot.js
+2
-5
@@ -16,7 +16,6 @@ import type {
16
import {isValidContainer} from 'react-dom-bindings/src/client/ReactDOMContainer';
17
import {queueExplicitHydrationTarget} from 'react-dom-bindings/src/events/ReactDOMEventReplaying';
18
import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
19
-import {enableAsyncActions} from 'shared/ReactFeatureFlags';
19
20
export type RootType = {
21
render(children: ReactNodeList): void,
@@ -305,10 +304,8 @@ export function hydrateRoot(
304
if (options.unstable_transitionCallbacks !== undefined) {
305
transitionCallbacks = options.unstable_transitionCallbacks;
306
}
308
- if (enableAsyncActions) {
309
- if (options.formState !== undefined) {
310
- formState = options.formState;
311
- }
307
+ if (options.formState !== undefined) {
308
+ formState = options.formState;
309
}
310
}
311
packages/react-reconciler/src/ReactFiberBeginWork.js
+46
-49
@@ -106,7 +106,6 @@ import {
106
enableTransitionTracing,
107
enableLegacyHidden,
108
enableCPUSuspense,
109
- enableAsyncActions,
109
enablePostpone,
110
enableRenderableContext,
111
disableLegacyMode,
@@ -1619,55 +1618,53 @@ function updateHostComponent(
1618
workInProgress.flags |= ContentReset;
1619
}
1620
1622
- if (enableAsyncActions) {
1623
- const memoizedState = workInProgress.memoizedState;
1624
- if (memoizedState !== null) {
1625
- // This fiber has been upgraded to a stateful component. The only way
1626
- // happens currently is for form actions. We use hooks to track the
1627
- // pending and error state of the form.
1628
- //
1629
- // Once a fiber is upgraded to be stateful, it remains stateful for the
1630
- // rest of its lifetime.
1631
- const newState = renderTransitionAwareHostComponentWithHooks(
1632
- current,
1633
- workInProgress,
1634
- renderLanes,
1635
- );
1621
+ const memoizedState = workInProgress.memoizedState;
1622
+ if (memoizedState !== null) {
1623
+ // This fiber has been upgraded to a stateful component. The only way
1624
+ // happens currently is for form actions. We use hooks to track the
1625
+ // pending and error state of the form.
1626
+ //
1627
+ // Once a fiber is upgraded to be stateful, it remains stateful for the
1628
+ // rest of its lifetime.
1629
+ const newState = renderTransitionAwareHostComponentWithHooks(
1630
+ current,
1631
+ workInProgress,
1632
+ renderLanes,
1633
+ );
1634
1637
- // If the transition state changed, propagate the change to all the
1638
- // descendents. We use Context as an implementation detail for this.
1639
- //
1640
- // This is intentionally set here instead of pushHostContext because
1641
- // pushHostContext gets called before we process the state hook, to avoid
1642
- // a state mismatch in the event that something suspends.
1643
- //
1644
- // NOTE: This assumes that there cannot be nested transition providers,
1645
- // because the only renderer that implements this feature is React DOM,
1646
- // and forms cannot be nested. If we did support nested providers, then
1647
- // we would need to push a context value even for host fibers that
1648
- // haven't been upgraded yet.
1649
- if (isPrimaryRenderer) {
1650
- HostTransitionContext._currentValue = newState;
1651
- } else {
1652
- HostTransitionContext._currentValue2 = newState;
1653
- }
1654
- if (enableLazyContextPropagation) {
1655
- // In the lazy propagation implementation, we don't scan for matching
1656
- // consumers until something bails out.
1657
- } else {
1658
- if (didReceiveUpdate) {
1659
- if (current !== null) {
1660
- const oldStateHook: Hook = current.memoizedState;
1661
- const oldState: TransitionStatus = oldStateHook.memoizedState;
1662
- // This uses regular equality instead of Object.is because we assume
1663
- // that host transition state doesn't include NaN as a valid type.
1664
- if (oldState !== newState) {
1665
- propagateContextChange(
1666
- workInProgress,
1667
- HostTransitionContext,
1668
- renderLanes,
1669
- );
1670
- }
1635
+ // If the transition state changed, propagate the change to all the
1636
+ // descendents. We use Context as an implementation detail for this.
1637
+ //
1638
+ // This is intentionally set here instead of pushHostContext because
1639
+ // pushHostContext gets called before we process the state hook, to avoid
1640
+ // a state mismatch in the event that something suspends.
1641
+ //
1642
+ // NOTE: This assumes that there cannot be nested transition providers,
1643
+ // because the only renderer that implements this feature is React DOM,
1644
+ // and forms cannot be nested. If we did support nested providers, then
1645
+ // we would need to push a context value even for host fibers that
1646
+ // haven't been upgraded yet.
1647
+ if (isPrimaryRenderer) {
1648
+ HostTransitionContext._currentValue = newState;
1649
+ } else {
1650
+ HostTransitionContext._currentValue2 = newState;
1651
+ }
1652
+ if (enableLazyContextPropagation) {
1653
+ // In the lazy propagation implementation, we don't scan for matching
1654
+ // consumers until something bails out.
1655
+ } else {
1656
+ if (didReceiveUpdate) {
1657
+ if (current !== null) {
1658
+ const oldStateHook: Hook = current.memoizedState;
1659
+ const oldState: TransitionStatus = oldStateHook.memoizedState;
1660
+ // This uses regular equality instead of Object.is because we assume
1661
+ // that host transition state doesn't include NaN as a valid type.
1662
+ if (oldState !== newState) {
1663
+ propagateContextChange(
1664
+ workInProgress,
1665
+ HostTransitionContext,
1666
+ renderLanes,
1667
+ );
1668
}
1669
}
1670
}
packages/react-reconciler/src/ReactFiberHooks.js
+270
-394
@@ -44,7 +44,6 @@ import {
44
enableUseEffectEventHook,
45
enableLegacyCache,
46
debugRenderPhaseSideEffectsForStrictMode,
47
- enableAsyncActions,
47
disableLegacyMode,
48
enableNoCloningMemoCache,
49
enableContextProfiling,
@@ -907,9 +906,6 @@ export function renderTransitionAwareHostComponentWithHooks(
906
workInProgress: Fiber,
907
lanes: Lanes,
908
): TransitionStatus {
910
- if (!enableAsyncActions) {
911
- throw new Error('Not implemented.');
912
- }
909
return renderWithHooks(
910
current,
911
workInProgress,
@@ -921,10 +917,6 @@ export function renderTransitionAwareHostComponentWithHooks(
917
}
918
919
export function TransitionAwareHostComponent(): TransitionStatus {
924
- if (!enableAsyncActions) {
925
- throw new Error('Not implemented.');
926
- }
927
-
920
const dispatcher: any = ReactSharedInternals.H;
921
const [maybeThenable] = dispatcher.useState();
922
let nextState;
@@ -1490,7 +1482,7 @@ function updateReducerImpl<S, A>(
1482
1483
// Check if this is an optimistic update.
1484
const revertLane = update.revertLane;
1493
- if (!enableAsyncActions || revertLane === NoLane) {
1485
+ if (revertLane === NoLane) {
1486
// This is not an optimistic update, and we're going to apply it now.
1487
// But, if there were earlier updates that were skipped, we need to
1488
// leave this update in the queue so it can be rebased later.
@@ -3268,25 +3260,14 @@ function startTransition<S>(
3260
const prevTransition = ReactSharedInternals.T;
3261
const currentTransition: BatchConfigTransition = {};
3262
3271
- if (enableAsyncActions) {
3272
- // We don't really need to use an optimistic update here, because we
3273
- // schedule a second "revert" update below (which we use to suspend the
3274
- // transition until the async action scope has finished). But we'll use an
3275
- // optimistic update anyway to make it less likely the behavior accidentally
3276
- // diverges; for example, both an optimistic update and this one should
3277
- // share the same lane.
3278
- ReactSharedInternals.T = currentTransition;
3279
- dispatchOptimisticSetState(fiber, false, queue, pendingState);
3280
- } else {
3281
- ReactSharedInternals.T = null;
3282
- dispatchSetStateInternal(
3283
- fiber,
3284
- queue,
3285
- pendingState,
3286
- requestUpdateLane(fiber),
3287
- );
3288
- ReactSharedInternals.T = currentTransition;
3289
- }
3263
+ // We don't really need to use an optimistic update here, because we
3264
+ // schedule a second "revert" update below (which we use to suspend the
3265
+ // transition until the async action scope has finished). But we'll use an
3266
+ // optimistic update anyway to make it less likely the behavior accidentally
3267
+ // diverges; for example, both an optimistic update and this one should
3268
+ // share the same lane.
3269
+ ReactSharedInternals.T = currentTransition;
3270
+ dispatchOptimisticSetState(fiber, false, queue, pendingState);
3271
3272
if (enableTransitionTracing) {
3273
if (options !== undefined && options.name !== undefined) {
@@ -3300,78 +3281,61 @@ function startTransition<S>(
3281
}
3282
3283
try {
3303
- if (enableAsyncActions) {
3304
- const returnValue = callback();
3305
- const onStartTransitionFinish = ReactSharedInternals.S;
3306
- if (onStartTransitionFinish !== null) {
3307
- onStartTransitionFinish(currentTransition, returnValue);
3308
- }
3284
+ const returnValue = callback();
3285
+ const onStartTransitionFinish = ReactSharedInternals.S;
3286
+ if (onStartTransitionFinish !== null) {
3287
+ onStartTransitionFinish(currentTransition, returnValue);
3288
+ }
3289
3310
- // Check if we're inside an async action scope. If so, we'll entangle
3311
- // this new action with the existing scope.
3312
- //
3313
- // If we're not already inside an async action scope, and this action is
3314
- // async, then we'll create a new async scope.
3315
- //
3316
- // In the async case, the resulting render will suspend until the async
3317
- // action scope has finished.
3318
- if (
3319
- returnValue !== null &&
3320
- typeof returnValue === 'object' &&
3321
- typeof returnValue.then === 'function'
3322
- ) {
3323
- const thenable = ((returnValue: any): Thenable<mixed>);
3324
- // Create a thenable that resolves to `finishedState` once the async
3325
- // action has completed.
3326
- const thenableForFinishedState = chainThenableValue(
3327
- thenable,
3328
- finishedState,
3329
- );
3330
- dispatchSetStateInternal(
3331
- fiber,
3332
- queue,
3333
- (thenableForFinishedState: any),
3334
- requestUpdateLane(fiber),
3335
- );
3336
- } else {
3337
- dispatchSetStateInternal(
3338
- fiber,
3339
- queue,
3340
- finishedState,
3341
- requestUpdateLane(fiber),
3342
- );
3343
- }
3344
- } else {
3345
- // Async actions are not enabled.
3290
+ // Check if we're inside an async action scope. If so, we'll entangle
3291
+ // this new action with the existing scope.
3292
+ //
3293
+ // If we're not already inside an async action scope, and this action is
3294
+ // async, then we'll create a new async scope.
3295
+ //
3296
+ // In the async case, the resulting render will suspend until the async
3297
+ // action scope has finished.
3298
+ if (
3299
+ returnValue !== null &&
3300
+ typeof returnValue === 'object' &&
3301
+ typeof returnValue.then === 'function'
3302
+ ) {
3303
+ const thenable = ((returnValue: any): Thenable<mixed>);
3304
+ // Create a thenable that resolves to `finishedState` once the async
3305
+ // action has completed.
3306
+ const thenableForFinishedState = chainThenableValue(
3307
+ thenable,
3308
+ finishedState,
3309
+ );
3310
dispatchSetStateInternal(
3311
fiber,
3312
queue,
3349
- finishedState,
3313
+ (thenableForFinishedState: any),
3314
requestUpdateLane(fiber),
3315
);
3352
- callback();
3353
- }
3354
- } catch (error) {
3355
- if (enableAsyncActions) {
3356
- // This is a trick to get the `useTransition` hook to rethrow the error.
3357
- // When it unwraps the thenable with the `use` algorithm, the error
3358
- // will be thrown.
3359
- const rejectedThenable: RejectedThenable<S> = {
3360
- then() {},
3361
- status: 'rejected',
3362
- reason: error,
3363
- };
3316
+ } else {
3317
dispatchSetStateInternal(
3318
fiber,
3319
queue,
3367
- rejectedThenable,
3320
+ finishedState,
3321
requestUpdateLane(fiber),
3322
);
3370
- } else {
3371
- // The error rethrowing behavior is only enabled when the async actions
3372
- // feature is on, even for sync actions.
3373
- throw error;
3323
}
3324
+ } catch (error) {
3325
+ // This is a trick to get the `useTransition` hook to rethrow the error.
3326
+ // When it unwraps the thenable with the `use` algorithm, the error
3327
+ // will be thrown.
3328
+ const rejectedThenable: RejectedThenable<S> = {
3329
+ then() {},
3330
+ status: 'rejected',
3331
+ reason: error,
3332
+ };
3333
+ dispatchSetStateInternal(
3334
+ fiber,
3335
+ queue,
3336
+ rejectedThenable,
3337
+ requestUpdateLane(fiber),
3338
+ );
3339
} finally {
3340
setCurrentUpdatePriority(previousPriority);
3341
@@ -3401,15 +3365,6 @@ export function startHostTransition<F>(
3365
action: (F => mixed) | null,
3366
formData: F,
3367
): void {
3404
- if (!enableAsyncActions) {
3405
- // Form actions are enabled, but async actions are not. Call the function,
3406
- // but don't handle any pending or error states.
3407
- if (action !== null) {
3408
- action(formData);
3409
- }
3410
- return;
3411
- }
3412
-
3368
if (formFiber.tag !== HostComponent) {
3369
throw new Error(
3370
'Expected the form instance to be a HostComponent. This ' +
@@ -3595,9 +3550,6 @@ function rerenderTransition(): [
3550
}
3551
3552
function useHostTransitionStatus(): TransitionStatus {
3598
- if (!enableAsyncActions) {
3599
- throw new Error('Not implemented.');
3600
- }
3553
return readContext(HostTransitionContext);
3554
}
3555
@@ -4026,6 +3978,10 @@ export const ContextOnlyDispatcher: Dispatcher = {
3978
useTransition: throwInvalidHookError,
3979
useSyncExternalStore: throwInvalidHookError,
3980
useId: throwInvalidHookError,
3981
+ useHostTransitionStatus: throwInvalidHookError,
3982
+ useFormState: throwInvalidHookError,
3983
+ useActionState: throwInvalidHookError,
3984
+ useOptimistic: throwInvalidHookError,
3985
};
3986
if (enableCache) {
3987
(ContextOnlyDispatcher: Dispatcher).useCacheRefresh = throwInvalidHookError;
@@ -4039,15 +3995,6 @@ if (enableUseEffectEventHook) {
3995
if (enableUseResourceEffectHook) {
3996
(ContextOnlyDispatcher: Dispatcher).useResourceEffect = throwInvalidHookError;
3997
}
4042
-if (enableAsyncActions) {
4043
- (ContextOnlyDispatcher: Dispatcher).useHostTransitionStatus =
4044
- throwInvalidHookError;
4045
- (ContextOnlyDispatcher: Dispatcher).useFormState = throwInvalidHookError;
4046
- (ContextOnlyDispatcher: Dispatcher).useActionState = throwInvalidHookError;
4047
-}
4048
-if (enableAsyncActions) {
4049
- (ContextOnlyDispatcher: Dispatcher).useOptimistic = throwInvalidHookError;
4050
-}
3998
if (enableContextProfiling) {
3999
(ContextOnlyDispatcher: Dispatcher).unstable_useContextWithBailout =
4000
throwInvalidHookError;
@@ -4072,6 +4019,10 @@ const HooksDispatcherOnMount: Dispatcher = {
4019
useTransition: mountTransition,
4020
useSyncExternalStore: mountSyncExternalStore,
4021
useId: mountId,
4022
+ useHostTransitionStatus: useHostTransitionStatus,
4023
+ useFormState: mountActionState,
4024
+ useActionState: mountActionState,
4025
+ useOptimistic: mountOptimistic,
4026
};
4027
if (enableCache) {
4028
(HooksDispatcherOnMount: Dispatcher).useCacheRefresh = mountRefresh;
@@ -4085,15 +4036,6 @@ if (enableUseEffectEventHook) {
4036
if (enableUseResourceEffectHook) {
4037
(HooksDispatcherOnMount: Dispatcher).useResourceEffect = mountResourceEffect;
4038
}
4088
-if (enableAsyncActions) {
4089
- (HooksDispatcherOnMount: Dispatcher).useHostTransitionStatus =
4090
- useHostTransitionStatus;
4091
- (HooksDispatcherOnMount: Dispatcher).useFormState = mountActionState;
4092
- (HooksDispatcherOnMount: Dispatcher).useActionState = mountActionState;
4093
-}
4094
-if (enableAsyncActions) {
4095
- (HooksDispatcherOnMount: Dispatcher).useOptimistic = mountOptimistic;
4096
-}
4039
if (enableContextProfiling) {
4040
(HooksDispatcherOnMount: Dispatcher).unstable_useContextWithBailout =
4041
unstable_useContextWithBailout;
@@ -4118,6 +4060,10 @@ const HooksDispatcherOnUpdate: Dispatcher = {
4060
useTransition: updateTransition,
4061
useSyncExternalStore: updateSyncExternalStore,
4062
useId: updateId,
4063
+ useHostTransitionStatus: useHostTransitionStatus,
4064
+ useFormState: updateActionState,
4065
+ useActionState: updateActionState,
4066
+ useOptimistic: updateOptimistic,
4067
};
4068
if (enableCache) {
4069
(HooksDispatcherOnUpdate: Dispatcher).useCacheRefresh = updateRefresh;
@@ -4132,15 +4078,6 @@ if (enableUseResourceEffectHook) {
4078
(HooksDispatcherOnUpdate: Dispatcher).useResourceEffect =
4079
updateResourceEffect;
4080
}
4135
-if (enableAsyncActions) {
4136
- (HooksDispatcherOnUpdate: Dispatcher).useHostTransitionStatus =
4137
- useHostTransitionStatus;
4138
- (HooksDispatcherOnUpdate: Dispatcher).useFormState = updateActionState;
4139
- (HooksDispatcherOnUpdate: Dispatcher).useActionState = updateActionState;
4140
-}
4141
-if (enableAsyncActions) {
4142
- (HooksDispatcherOnUpdate: Dispatcher).useOptimistic = updateOptimistic;
4143
-}
4081
if (enableContextProfiling) {
4082
(HooksDispatcherOnUpdate: Dispatcher).unstable_useContextWithBailout =
4083
unstable_useContextWithBailout;
@@ -4165,6 +4102,10 @@ const HooksDispatcherOnRerender: Dispatcher = {
4102
useTransition: rerenderTransition,
4103
useSyncExternalStore: updateSyncExternalStore,
4104
useId: updateId,
4105
+ useHostTransitionStatus: useHostTransitionStatus,
4106
+ useFormState: rerenderActionState,
4107
+ useActionState: rerenderActionState,
4108
+ useOptimistic: rerenderOptimistic,
4109
};
4110
if (enableCache) {
4111
(HooksDispatcherOnRerender: Dispatcher).useCacheRefresh = updateRefresh;
@@ -4179,15 +4120,6 @@ if (enableUseResourceEffectHook) {
4120
(HooksDispatcherOnRerender: Dispatcher).useResourceEffect =
4121
updateResourceEffect;
4122
}
4182
-if (enableAsyncActions) {
4183
- (HooksDispatcherOnRerender: Dispatcher).useHostTransitionStatus =
4184
- useHostTransitionStatus;
4185
- (HooksDispatcherOnRerender: Dispatcher).useFormState = rerenderActionState;
4186
- (HooksDispatcherOnRerender: Dispatcher).useActionState = rerenderActionState;
4187
-}
4188
-if (enableAsyncActions) {
4189
- (HooksDispatcherOnRerender: Dispatcher).useOptimistic = rerenderOptimistic;
4190
-}
4123
if (enableContextProfiling) {
4124
(HooksDispatcherOnRerender: Dispatcher).unstable_useContextWithBailout =
4125
unstable_useContextWithBailout;
@@ -4347,6 +4279,34 @@ if (__DEV__) {
4279
mountHookTypesDev();
4280
return mountId();
4281
},
4282
+ useFormState<S, P>(
4283
+ action: (Awaited<S>, P) => S,
4284
+ initialState: Awaited<S>,
4285
+ permalink?: string,
4286
+ ): [Awaited<S>, (P) => void, boolean] {
4287
+ currentHookNameInDev = 'useFormState';
4288
+ mountHookTypesDev();
4289
+ warnOnUseFormStateInDev();
4290
+ return mountActionState(action, initialState, permalink);
4291
+ },
4292
+ useActionState<S, P>(
4293
+ action: (Awaited<S>, P) => S,
4294
+ initialState: Awaited<S>,
4295
+ permalink?: string,
4296
+ ): [Awaited<S>, (P) => void, boolean] {
4297
+ currentHookNameInDev = 'useActionState';
4298
+ mountHookTypesDev();
4299
+ return mountActionState(action, initialState, permalink);
4300
+ },
4301
+ useOptimistic<S, A>(
4302
+ passthrough: S,
4303
+ reducer: ?(S, A) => S,
4304
+ ): [S, (A) => void] {
4305
+ currentHookNameInDev = 'useOptimistic';
4306
+ mountHookTypesDev();
4307
+ return mountOptimistic(passthrough, reducer);
4308
+ },
4309
+ useHostTransitionStatus,
4310
};
4311
if (enableCache) {
4312
(HooksDispatcherOnMountInDEV: Dispatcher).useCacheRefresh =
@@ -4390,42 +4350,6 @@ if (__DEV__) {
4350
);
4351
};
4352
}
4393
- if (enableAsyncActions) {
4394
- (HooksDispatcherOnMountInDEV: Dispatcher).useHostTransitionStatus =
4395
- useHostTransitionStatus;
4396
- (HooksDispatcherOnMountInDEV: Dispatcher).useFormState =
4397
- function useFormState<S, P>(
4398
- action: (Awaited<S>, P) => S,
4399
- initialState: Awaited<S>,
4400
- permalink?: string,
4401
- ): [Awaited<S>, (P) => void, boolean] {
4402
- currentHookNameInDev = 'useFormState';
4403
- mountHookTypesDev();
4404
- warnOnUseFormStateInDev();
4405
- return mountActionState(action, initialState, permalink);
4406
- };
4407
- (HooksDispatcherOnMountInDEV: Dispatcher).useActionState =
4408
- function useActionState<S, P>(
4409
- action: (Awaited<S>, P) => S,
4410
- initialState: Awaited<S>,
4411
- permalink?: string,
4412
- ): [Awaited<S>, (P) => void, boolean] {
4413
- currentHookNameInDev = 'useActionState';
4414
- mountHookTypesDev();
4415
- return mountActionState(action, initialState, permalink);
4416
- };
4417
- }
4418
- if (enableAsyncActions) {
4419
- (HooksDispatcherOnMountInDEV: Dispatcher).useOptimistic =
4420
- function useOptimistic<S, A>(
4421
- passthrough: S,
4422
- reducer: ?(S, A) => S,
4423
- ): [S, (A) => void] {
4424
- currentHookNameInDev = 'useOptimistic';
4425
- mountHookTypesDev();
4426
- return mountOptimistic(passthrough, reducer);
4427
- };
4428
- }
4353
if (enableContextProfiling) {
4354
(HooksDispatcherOnMountInDEV: Dispatcher).unstable_useContextWithBailout =
4355
function <T>(
@@ -4559,6 +4483,34 @@ if (__DEV__) {
4483
updateHookTypesDev();
4484
return mountId();
4485
},
4486
+ useActionState<S, P>(
4487
+ action: (Awaited<S>, P) => S,
4488
+ initialState: Awaited<S>,
4489
+ permalink?: string,
4490
+ ): [Awaited<S>, (P) => void, boolean] {
4491
+ currentHookNameInDev = 'useActionState';
4492
+ updateHookTypesDev();
4493
+ return mountActionState(action, initialState, permalink);
4494
+ },
4495
+ useFormState<S, P>(
4496
+ action: (Awaited<S>, P) => S,
4497
+ initialState: Awaited<S>,
4498
+ permalink?: string,
4499
+ ): [Awaited<S>, (P) => void, boolean] {
4500
+ currentHookNameInDev = 'useFormState';
4501
+ updateHookTypesDev();
4502
+ warnOnUseFormStateInDev();
4503
+ return mountActionState(action, initialState, permalink);
4504
+ },
4505
+ useOptimistic<S, A>(
4506
+ passthrough: S,
4507
+ reducer: ?(S, A) => S,
4508
+ ): [S, (A) => void] {
4509
+ currentHookNameInDev = 'useOptimistic';
4510
+ updateHookTypesDev();
4511
+ return mountOptimistic(passthrough, reducer);
4512
+ },
4513
+ useHostTransitionStatus,
4514
};
4515
if (enableCache) {
4516
(HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useCacheRefresh =
@@ -4602,42 +4554,6 @@ if (__DEV__) {
4554
);
4555
};
4556
}
4605
- if (enableAsyncActions) {
4606
- (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useHostTransitionStatus =
4607
- useHostTransitionStatus;
4608
- (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useFormState =
4609
- function useFormState<S, P>(
4610
- action: (Awaited<S>, P) => S,
4611
- initialState: Awaited<S>,
4612
- permalink?: string,
4613
- ): [Awaited<S>, (P) => void, boolean] {
4614
- currentHookNameInDev = 'useFormState';
4615
- updateHookTypesDev();
4616
- warnOnUseFormStateInDev();
4617
- return mountActionState(action, initialState, permalink);
4618
- };
4619
- (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useActionState =
4620
- function useActionState<S, P>(
4621
- action: (Awaited<S>, P) => S,
4622
- initialState: Awaited<S>,
4623
- permalink?: string,
4624
- ): [Awaited<S>, (P) => void, boolean] {
4625
- currentHookNameInDev = 'useActionState';
4626
- updateHookTypesDev();
4627
- return mountActionState(action, initialState, permalink);
4628
- };
4629
- }
4630
- if (enableAsyncActions) {
4631
- (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useOptimistic =
4632
- function useOptimistic<S, A>(
4633
- passthrough: S,
4634
- reducer: ?(S, A) => S,
4635
- ): [S, (A) => void] {
4636
- currentHookNameInDev = 'useOptimistic';
4637
- updateHookTypesDev();
4638
- return mountOptimistic(passthrough, reducer);
4639
- };
4640
- }
4557
if (enableContextProfiling) {
4558
(HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).unstable_useContextWithBailout =
4559
function <T>(
@@ -4771,6 +4687,34 @@ if (__DEV__) {
4687
updateHookTypesDev();
4688
return updateId();
4689
},
4690
+ useFormState<S, P>(
4691
+ action: (Awaited<S>, P) => S,
4692
+ initialState: Awaited<S>,
4693
+ permalink?: string,
4694
+ ): [Awaited<S>, (P) => void, boolean] {
4695
+ currentHookNameInDev = 'useFormState';
4696
+ updateHookTypesDev();
4697
+ warnOnUseFormStateInDev();
4698
+ return updateActionState(action, initialState, permalink);
4699
+ },
4700
+ useActionState<S, P>(
4701
+ action: (Awaited<S>, P) => S,
4702
+ initialState: Awaited<S>,
4703
+ permalink?: string,
4704
+ ): [Awaited<S>, (P) => void, boolean] {
4705
+ currentHookNameInDev = 'useActionState';
4706
+ updateHookTypesDev();
4707
+ return updateActionState(action, initialState, permalink);
4708
+ },
4709
+ useOptimistic<S, A>(
4710
+ passthrough: S,
4711
+ reducer: ?(S, A) => S,
4712
+ ): [S, (A) => void] {
4713
+ currentHookNameInDev = 'useOptimistic';
4714
+ updateHookTypesDev();
4715
+ return updateOptimistic(passthrough, reducer);
4716
+ },
4717
+ useHostTransitionStatus,
4718
};
4719
if (enableCache) {
4720
(HooksDispatcherOnUpdateInDEV: Dispatcher).useCacheRefresh =
@@ -4813,42 +4757,6 @@ if (__DEV__) {
4757
);
4758
};
4759
}
4816
- if (enableAsyncActions) {
4817
- (HooksDispatcherOnUpdateInDEV: Dispatcher).useHostTransitionStatus =
4818
- useHostTransitionStatus;
4819
- (HooksDispatcherOnUpdateInDEV: Dispatcher).useFormState =
4820
- function useFormState<S, P>(
4821
- action: (Awaited<S>, P) => S,
4822
- initialState: Awaited<S>,
4823
- permalink?: string,
4824
- ): [Awaited<S>, (P) => void, boolean] {
4825
- currentHookNameInDev = 'useFormState';
4826
- updateHookTypesDev();
4827
- warnOnUseFormStateInDev();
4828
- return updateActionState(action, initialState, permalink);
4829
- };
4830
- (HooksDispatcherOnUpdateInDEV: Dispatcher).useActionState =
4831
- function useActionState<S, P>(
4832
- action: (Awaited<S>, P) => S,
4833
- initialState: Awaited<S>,
4834
- permalink?: string,
4835
- ): [Awaited<S>, (P) => void, boolean] {
4836
- currentHookNameInDev = 'useActionState';
4837
- updateHookTypesDev();
4838
- return updateActionState(action, initialState, permalink);
4839
- };
4840
- }
4841
- if (enableAsyncActions) {
4842
- (HooksDispatcherOnUpdateInDEV: Dispatcher).useOptimistic =
4843
- function useOptimistic<S, A>(
4844
- passthrough: S,
4845
- reducer: ?(S, A) => S,
4846
- ): [S, (A) => void] {
4847
- currentHookNameInDev = 'useOptimistic';
4848
- updateHookTypesDev();
4849
- return updateOptimistic(passthrough, reducer);
4850
- };
4851
- }
4760
if (enableContextProfiling) {
4761
(HooksDispatcherOnUpdateInDEV: Dispatcher).unstable_useContextWithBailout =
4762
function <T>(
@@ -4982,6 +4890,34 @@ if (__DEV__) {
4890
updateHookTypesDev();
4891
return updateId();
4892
},
4893
+ useFormState<S, P>(
4894
+ action: (Awaited<S>, P) => S,
4895
+ initialState: Awaited<S>,
4896
+ permalink?: string,
4897
+ ): [Awaited<S>, (P) => void, boolean] {
4898
+ currentHookNameInDev = 'useFormState';
4899
+ updateHookTypesDev();
4900
+ warnOnUseFormStateInDev();
4901
+ return rerenderActionState(action, initialState, permalink);
4902
+ },
4903
+ useActionState<S, P>(
4904
+ action: (Awaited<S>, P) => S,
4905
+ initialState: Awaited<S>,
4906
+ permalink?: string,
4907
+ ): [Awaited<S>, (P) => void, boolean] {
4908
+ currentHookNameInDev = 'useActionState';
4909
+ updateHookTypesDev();
4910
+ return rerenderActionState(action, initialState, permalink);
4911
+ },
4912
+ useOptimistic<S, A>(
4913
+ passthrough: S,
4914
+ reducer: ?(S, A) => S,
4915
+ ): [S, (A) => void] {
4916
+ currentHookNameInDev = 'useOptimistic';
4917
+ updateHookTypesDev();
4918
+ return rerenderOptimistic(passthrough, reducer);
4919
+ },
4920
+ useHostTransitionStatus,
4921
};
4922
if (enableCache) {
4923
(HooksDispatcherOnRerenderInDEV: Dispatcher).useCacheRefresh =
@@ -5024,42 +4960,6 @@ if (__DEV__) {
4960
);
4961
};
4962
}
5027
- if (enableAsyncActions) {
5028
- (HooksDispatcherOnRerenderInDEV: Dispatcher).useHostTransitionStatus =
5029
- useHostTransitionStatus;
5030
- (HooksDispatcherOnRerenderInDEV: Dispatcher).useFormState =
5031
- function useFormState<S, P>(
5032
- action: (Awaited<S>, P) => S,
5033
- initialState: Awaited<S>,
5034
- permalink?: string,
5035
- ): [Awaited<S>, (P) => void, boolean] {
5036
- currentHookNameInDev = 'useFormState';
5037
- updateHookTypesDev();
5038
- warnOnUseFormStateInDev();
5039
- return rerenderActionState(action, initialState, permalink);
5040
- };
5041
- (HooksDispatcherOnRerenderInDEV: Dispatcher).useActionState =
5042
- function useActionState<S, P>(
5043
- action: (Awaited<S>, P) => S,
5044
- initialState: Awaited<S>,
5045
- permalink?: string,
5046
- ): [Awaited<S>, (P) => void, boolean] {
5047
- currentHookNameInDev = 'useActionState';
5048
- updateHookTypesDev();
5049
- return rerenderActionState(action, initialState, permalink);
5050
- };
5051
- }
5052
- if (enableAsyncActions) {
5053
- (HooksDispatcherOnRerenderInDEV: Dispatcher).useOptimistic =
5054
- function useOptimistic<S, A>(
5055
- passthrough: S,
5056
- reducer: ?(S, A) => S,
5057
- ): [S, (A) => void] {
5058
- currentHookNameInDev = 'useOptimistic';
5059
- updateHookTypesDev();
5060
- return rerenderOptimistic(passthrough, reducer);
5061
- };
5062
- }
4963
if (enableContextProfiling) {
4964
(HooksDispatcherOnRerenderInDEV: Dispatcher).unstable_useContextWithBailout =
4965
function <T>(
@@ -5212,6 +5112,36 @@ if (__DEV__) {
5112
mountHookTypesDev();
5113
return mountId();
5114
},
5115
+ useFormState<S, P>(
5116
+ action: (Awaited<S>, P) => S,
5117
+ initialState: Awaited<S>,
5118
+ permalink?: string,
5119
+ ): [Awaited<S>, (P) => void, boolean] {
5120
+ currentHookNameInDev = 'useFormState';
5121
+ warnInvalidHookAccess();
5122
+ mountHookTypesDev();
5123
+ return mountActionState(action, initialState, permalink);
5124
+ },
5125
+ useActionState<S, P>(
5126
+ action: (Awaited<S>, P) => S,
5127
+ initialState: Awaited<S>,
5128
+ permalink?: string,
5129
+ ): [Awaited<S>, (P) => void, boolean] {
5130
+ currentHookNameInDev = 'useActionState';
5131
+ warnInvalidHookAccess();
5132
+ mountHookTypesDev();
5133
+ return mountActionState(action, initialState, permalink);
5134
+ },
5135
+ useOptimistic<S, A>(
5136
+ passthrough: S,
5137
+ reducer: ?(S, A) => S,
5138
+ ): [S, (A) => void] {
5139
+ currentHookNameInDev = 'useOptimistic';
5140
+ warnInvalidHookAccess();
5141
+ mountHookTypesDev();
5142
+ return mountOptimistic(passthrough, reducer);
5143
+ },
5144
+ useHostTransitionStatus,
5145
};
5146
if (enableCache) {
5147
(InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useCacheRefresh =
@@ -5260,44 +5190,6 @@ if (__DEV__) {
5190
);
5191
};
5192
}
5263
- if (enableAsyncActions) {
5264
- (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useHostTransitionStatus =
5265
- useHostTransitionStatus;
5266
- (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useFormState =
5267
- function useFormState<S, P>(
5268
- action: (Awaited<S>, P) => S,
5269
- initialState: Awaited<S>,
5270
- permalink?: string,
5271
- ): [Awaited<S>, (P) => void, boolean] {
5272
- currentHookNameInDev = 'useFormState';
5273
- warnInvalidHookAccess();
5274
- mountHookTypesDev();
5275
- return mountActionState(action, initialState, permalink);
5276
- };
5277
- (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useActionState =
5278
- function useActionState<S, P>(
5279
- action: (Awaited<S>, P) => S,
5280
- initialState: Awaited<S>,
5281
- permalink?: string,
5282
- ): [Awaited<S>, (P) => void, boolean] {
5283
- currentHookNameInDev = 'useActionState';
5284
- warnInvalidHookAccess();
5285
- mountHookTypesDev();
5286
- return mountActionState(action, initialState, permalink);
5287
- };
5288
- }
5289
- if (enableAsyncActions) {
5290
- (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useOptimistic =
5291
- function useOptimistic<S, A>(
5292
- passthrough: S,
5293
- reducer: ?(S, A) => S,
5294
- ): [S, (A) => void] {
5295
- currentHookNameInDev = 'useOptimistic';
5296
- warnInvalidHookAccess();
5297
- mountHookTypesDev();
5298
- return mountOptimistic(passthrough, reducer);
5299
- };
5300
- }
5193
if (enableContextProfiling) {
5194
(InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).unstable_useContextWithBailout =
5195
function <T>(
@@ -5451,6 +5343,36 @@ if (__DEV__) {
5343
updateHookTypesDev();
5344
return updateId();
5345
},
5346
+ useFormState<S, P>(
5347
+ action: (Awaited<S>, P) => S,
5348
+ initialState: Awaited<S>,
5349
+ permalink?: string,
5350
+ ): [Awaited<S>, (P) => void, boolean] {
5351
+ currentHookNameInDev = 'useFormState';
5352
+ warnInvalidHookAccess();
5353
+ updateHookTypesDev();
5354
+ return updateActionState(action, initialState, permalink);
5355
+ },
5356
+ useActionState<S, P>(
5357
+ action: (Awaited<S>, P) => S,
5358
+ initialState: Awaited<S>,
5359
+ permalink?: string,
5360
+ ): [Awaited<S>, (P) => void, boolean] {
5361
+ currentHookNameInDev = 'useActionState';
5362
+ warnInvalidHookAccess();
5363
+ updateHookTypesDev();
5364
+ return updateActionState(action, initialState, permalink);
5365
+ },
5366
+ useOptimistic<S, A>(
5367
+ passthrough: S,
5368
+ reducer: ?(S, A) => S,
5369
+ ): [S, (A) => void] {
5370
+ currentHookNameInDev = 'useOptimistic';
5371
+ warnInvalidHookAccess();
5372
+ updateHookTypesDev();
5373
+ return updateOptimistic(passthrough, reducer);
5374
+ },
5375
+ useHostTransitionStatus,
5376
};
5377
if (enableCache) {
5378
(InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useCacheRefresh =
@@ -5499,44 +5421,6 @@ if (__DEV__) {
5421
);
5422
};
5423
}
5502
- if (enableAsyncActions) {
5503
- (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useHostTransitionStatus =
5504
- useHostTransitionStatus;
5505
- (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useFormState =
5506
- function useFormState<S, P>(
5507
- action: (Awaited<S>, P) => S,
5508
- initialState: Awaited<S>,
5509
- permalink?: string,
5510
- ): [Awaited<S>, (P) => void, boolean] {
5511
- currentHookNameInDev = 'useFormState';
5512
- warnInvalidHookAccess();
5513
- updateHookTypesDev();
5514
- return updateActionState(action, initialState, permalink);
5515
- };
5516
- (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useActionState =
5517
- function useActionState<S, P>(
5518
- action: (Awaited<S>, P) => S,
5519
- initialState: Awaited<S>,
5520
- permalink?: string,
5521
- ): [Awaited<S>, (P) => void, boolean] {
5522
- currentHookNameInDev = 'useActionState';
5523
- warnInvalidHookAccess();
5524
- updateHookTypesDev();
5525
- return updateActionState(action, initialState, permalink);
5526
- };
5527
- }
5528
- if (enableAsyncActions) {
5529
- (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useOptimistic =
5530
- function useOptimistic<S, A>(
5531
- passthrough: S,
5532
- reducer: ?(S, A) => S,
5533
- ): [S, (A) => void] {
5534
- currentHookNameInDev = 'useOptimistic';
5535
- warnInvalidHookAccess();
5536
- updateHookTypesDev();
5537
- return updateOptimistic(passthrough, reducer);
5538
- };
5539
- }
5424
if (enableContextProfiling) {
5425
(InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).unstable_useContextWithBailout =
5426
function <T>(
@@ -5690,6 +5574,36 @@ if (__DEV__) {
5574
updateHookTypesDev();
5575
return updateId();
5576
},
5577
+ useFormState<S, P>(
5578
+ action: (Awaited<S>, P) => S,
5579
+ initialState: Awaited<S>,
5580
+ permalink?: string,
5581
+ ): [Awaited<S>, (P) => void, boolean] {
5582
+ currentHookNameInDev = 'useFormState';
5583
+ warnInvalidHookAccess();
5584
+ updateHookTypesDev();
5585
+ return rerenderActionState(action, initialState, permalink);
5586
+ },
5587
+ useActionState<S, P>(
5588
+ action: (Awaited<S>, P) => S,
5589
+ initialState: Awaited<S>,
5590
+ permalink?: string,
5591
+ ): [Awaited<S>, (P) => void, boolean] {
5592
+ currentHookNameInDev = 'useActionState';
5593
+ warnInvalidHookAccess();
5594
+ updateHookTypesDev();
5595
+ return rerenderActionState(action, initialState, permalink);
5596
+ },
5597
+ useOptimistic<S, A>(
5598
+ passthrough: S,
5599
+ reducer: ?(S, A) => S,
5600
+ ): [S, (A) => void] {
5601
+ currentHookNameInDev = 'useOptimistic';
5602
+ warnInvalidHookAccess();
5603
+ updateHookTypesDev();
5604
+ return rerenderOptimistic(passthrough, reducer);
5605
+ },
5606
+ useHostTransitionStatus,
5607
};
5608
if (enableCache) {
5609
(InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useCacheRefresh =
@@ -5738,44 +5652,6 @@ if (__DEV__) {
5652
);
5653
};
5654
}
5741
- if (enableAsyncActions) {
5742
- (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useHostTransitionStatus =
5743
- useHostTransitionStatus;
5744
- (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useFormState =
5745
- function useFormState<S, P>(
5746
- action: (Awaited<S>, P) => S,
5747
- initialState: Awaited<S>,
5748
- permalink?: string,
5749
- ): [Awaited<S>, (P) => void, boolean] {
5750
- currentHookNameInDev = 'useFormState';
5751
- warnInvalidHookAccess();
5752
- updateHookTypesDev();
5753
- return rerenderActionState(action, initialState, permalink);
5754
- };
5755
- (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useActionState =
5756
- function useActionState<S, P>(
5757
- action: (Awaited<S>, P) => S,
5758
- initialState: Awaited<S>,
5759
- permalink?: string,
5760
- ): [Awaited<S>, (P) => void, boolean] {
5761
- currentHookNameInDev = 'useActionState';
5762
- warnInvalidHookAccess();
5763
- updateHookTypesDev();
5764
- return rerenderActionState(action, initialState, permalink);
5765
- };
5766
- }
5767
- if (enableAsyncActions) {
5768
- (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useOptimistic =
5769
- function useOptimistic<S, A>(
5770
- passthrough: S,
5771
- reducer: ?(S, A) => S,
5772
- ): [S, (A) => void] {
5773
- currentHookNameInDev = 'useOptimistic';
5774
- warnInvalidHookAccess();
5775
- updateHookTypesDev();
5776
- return rerenderOptimistic(passthrough, reducer);
5777
- };
5778
- }
5655
if (enableContextProfiling) {
5656
(InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).unstable_useContextWithBailout =
5657
function <T>(
packages/react-reconciler/src/ReactFiberHostContext.js
+22
-27
@@ -20,7 +20,6 @@ import {
20
isPrimaryRenderer,
21
} from './ReactFiberConfig';
22
import {createCursor, push, pop} from './ReactFiberStack';
23
-import {enableAsyncActions} from 'shared/ReactFeatureFlags';
23
24
const contextStackCursor: StackCursor<HostContext | null> = createCursor(null);
25
const contextFiberStackCursor: StackCursor<Fiber | null> = createCursor(null);
@@ -91,13 +90,11 @@ function getHostContext(): HostContext {
90
}
91
92
function pushHostContext(fiber: Fiber): void {
94
- if (enableAsyncActions) {
95
- const stateHook: Hook | null = fiber.memoizedState;
96
- if (stateHook !== null) {
97
- // Only provide context if this fiber has been upgraded by a host
98
- // transition. We use the same optimization for regular host context below.
99
- push(hostTransitionProviderCursor, fiber, fiber);
100
- }
93
+ const stateHook: Hook | null = fiber.memoizedState;
94
+ if (stateHook !== null) {
95
+ // Only provide context if this fiber has been upgraded by a host
96
+ // transition. We use the same optimization for regular host context below.
97
+ push(hostTransitionProviderCursor, fiber, fiber);
98
}
99
100
const context: HostContext = requiredContext(contextStackCursor.current);
@@ -120,25 +117,23 @@ function popHostContext(fiber: Fiber): void {
117
pop(contextFiberStackCursor, fiber);
118
}
119
123
- if (enableAsyncActions) {
124
- if (hostTransitionProviderCursor.current === fiber) {
125
- // Do not pop unless this Fiber provided the current context. This is mostly
126
- // a performance optimization, but conveniently it also prevents a potential
127
- // data race where a host provider is upgraded (i.e. memoizedState becomes
128
- // non-null) during a concurrent event. This is a bit of a flaw in the way
129
- // we upgrade host components, but because we're accounting for it here, it
130
- // should be fine.
131
- pop(hostTransitionProviderCursor, fiber);
132
-
133
- // When popping the transition provider, we reset the context value back
134
- // to `NotPendingTransition`. We can do this because you're not allowed to nest forms. If
135
- // we allowed for multiple nested host transition providers, then we'd
136
- // need to reset this to the parent provider's status.
137
- if (isPrimaryRenderer) {
138
- HostTransitionContext._currentValue = NotPendingTransition;
139
- } else {
140
- HostTransitionContext._currentValue2 = NotPendingTransition;
141
- }
120
+ if (hostTransitionProviderCursor.current === fiber) {
121
+ // Do not pop unless this Fiber provided the current context. This is mostly
122
+ // a performance optimization, but conveniently it also prevents a potential
123
+ // data race where a host provider is upgraded (i.e. memoizedState becomes
124
+ // non-null) during a concurrent event. This is a bit of a flaw in the way
125
+ // we upgrade host components, but because we're accounting for it here, it
126
+ // should be fine.
127
+ pop(hostTransitionProviderCursor, fiber);
128
+
129
+ // When popping the transition provider, we reset the context value back
130
+ // to `NotPendingTransition`. We can do this because you're not allowed to nest forms. If
131
+ // we allowed for multiple nested host transition providers, then we'd
132
+ // need to reset this to the parent provider's status.
133
+ if (isPrimaryRenderer) {
134
+ HostTransitionContext._currentValue = NotPendingTransition;
135
+ } else {
136
+ HostTransitionContext._currentValue2 = NotPendingTransition;
137
}
138
}
139
}
packages/react-reconciler/src/ReactFiberNewContext.js
+1
-2
@@ -45,7 +45,6 @@ import {createUpdate, ForceUpdate} from './ReactFiberClassUpdateQueue';
45
import {markWorkInProgressReceivedUpdate} from './ReactFiberBeginWork';
46
import {
47
enableLazyContextPropagation,
48
- enableAsyncActions,
48
enableRenderableContext,
49
} from 'shared/ReactFeatureFlags';
50
import {getHostTransitionProvider} from './ReactFiberHostContext';
@@ -598,7 +597,7 @@ function propagateParentContextChanges(
597
}
598
}
599
}
601
- } else if (enableAsyncActions && parent === getHostTransitionProvider()) {
600
+ } else if (parent === getHostTransitionProvider()) {
601
// During a host transition, a host component can act like a context
602
// provider. E.g. in React DOM, this would be a <form />.
603
const currentParent = parent.alternate;
packages/react-reconciler/src/ReactFiberTransition.js
+1
-6
@@ -16,11 +16,7 @@ import type {
16
Transition,
17
} from './ReactFiberTracingMarkerComponent';
18
19
-import {
20
- enableCache,
21
- enableTransitionTracing,
22
- enableAsyncActions,
23
-} from 'shared/ReactFeatureFlags';
19
+import {enableCache, enableTransitionTracing} from 'shared/ReactFeatureFlags';
20
import {isPrimaryRenderer} from './ReactFiberConfig';
21
import {createCursor, push, pop} from './ReactFiberStack';
22
import {
@@ -65,7 +61,6 @@ ReactSharedInternals.S = function onStartTransitionFinishForReconciler(
61
returnValue: mixed,
62
) {
63
if (
68
- enableAsyncActions &&
64
typeof returnValue === 'object' &&
65
returnValue !== null &&
66
typeof returnValue.then === 'function'
packages/react-reconciler/src/ReactInternalTypes.js
+4
-4
@@ -448,17 +448,17 @@ export type Dispatcher = {
448
useId(): string,
449
useCacheRefresh?: () => <T>(?() => T, ?T) => void,
450
useMemoCache?: (size: number) => Array<any>,
451
- useHostTransitionStatus?: () => TransitionStatus,
452
- useOptimistic?: <S, A>(
451
+ useHostTransitionStatus: () => TransitionStatus,
452
+ useOptimistic: <S, A>(
453
passthrough: S,
454
reducer: ?(S, A) => S,
455
) => [S, (A) => void],
456
- useFormState?: <S, P>(
456
+ useFormState: <S, P>(
457
action: (Awaited<S>, P) => S,
458
initialState: Awaited<S>,
459
permalink?: string,
460
) => [Awaited<S>, (P) => void, boolean],
461
- useActionState?: <S, P>(
461
+ useActionState: <S, P>(
462
action: (Awaited<S>, P) => S,
463
initialState: Awaited<S>,
464
permalink?: string,
packages/react-reconciler/src/__tests__/ReactAsyncActions-test.js
+5
-60
@@ -121,7 +121,6 @@ describe('ReactAsyncActions', () => {
121
return text;
122
}
123
124
- // @gate enableAsyncActions
124
it('isPending remains true until async action finishes', async () => {
125
let startTransition;
126
function App() {
@@ -154,7 +153,6 @@ describe('ReactAsyncActions', () => {
153
expect(root).toMatchRenderedOutput('Pending: false');
154
});
155
157
- // @gate enableAsyncActions
156
it('multiple updates in an async action scope are entangled together', async () => {
157
let startTransition;
158
function App({text}) {
@@ -210,7 +208,6 @@ describe('ReactAsyncActions', () => {
208
);
209
});
210
213
- // @gate enableAsyncActions
211
it('multiple async action updates in the same scope are entangled together', async () => {
212
let setStepA;
213
function A() {
@@ -350,7 +347,6 @@ describe('ReactAsyncActions', () => {
347
);
348
});
349
353
- // @gate enableAsyncActions
350
it('urgent updates are not blocked during an async action', async () => {
351
let setStepA;
352
function A() {
@@ -431,7 +427,6 @@ describe('ReactAsyncActions', () => {
427
);
428
});
429
434
- // @gate enableAsyncActions
430
it("if a sync action throws, it's rethrown from the `useTransition`", async () => {
431
class ErrorBoundary extends React.Component {
432
state = {error: null};
@@ -473,7 +468,6 @@ describe('ReactAsyncActions', () => {
468
expect(root).toMatchRenderedOutput('Oops!');
469
});
470
476
- // @gate enableAsyncActions
471
it("if an async action throws, it's rethrown from the `useTransition`", async () => {
472
class ErrorBoundary extends React.Component {
473
state = {error: null};
@@ -521,34 +515,6 @@ describe('ReactAsyncActions', () => {
515
expect(root).toMatchRenderedOutput('Oops!');
516
});
517
524
- // @gate !enableAsyncActions
525
- it('when enableAsyncActions is disabled, and a sync action throws, `isPending` is turned off', async () => {
526
- let startTransition;
527
- function App() {
528
- const [isPending, _start] = useTransition();
529
- startTransition = _start;
530
- return <Text text={'Pending: ' + isPending} />;
531
- }
532
-
533
- const root = ReactNoop.createRoot();
534
- await act(() => {
535
- root.render(<App />);
536
- });
537
- assertLog(['Pending: false']);
538
- expect(root).toMatchRenderedOutput('Pending: false');
539
-
540
- await act(() => {
541
- expect(() => {
542
- startTransition(() => {
543
- throw new Error('Oops!');
544
- });
545
- }).toThrow('Oops!');
546
- });
547
- assertLog(['Pending: true', 'Pending: false']);
548
- expect(root).toMatchRenderedOutput('Pending: false');
549
- });
550
-
551
- // @gate enableAsyncActions
518
it('if there are multiple entangled actions, and one of them errors, it only affects that action', async () => {
519
class ErrorBoundary extends React.Component {
520
state = {error: null};
@@ -665,7 +631,6 @@ describe('ReactAsyncActions', () => {
631
);
632
});
633
668
- // @gate enableAsyncActions
634
it('useOptimistic can be used to implement a pending state', async () => {
635
const startTransition = React.startTransition;
636
@@ -715,7 +680,6 @@ describe('ReactAsyncActions', () => {
680
]);
681
});
682
718
- // @gate enableAsyncActions
683
it('useOptimistic rebases pending updates on top of passthrough value', async () => {
684
let serverCart = ['A'];
685
@@ -836,7 +800,6 @@ describe('ReactAsyncActions', () => {
800
);
801
});
802
839
- // @gate enableAsyncActions
803
it(
804
'regression: when there are no pending transitions, useOptimistic should ' +
805
'always return the passthrough value',
@@ -882,7 +845,6 @@ describe('ReactAsyncActions', () => {
845
},
846
);
847
885
- // @gate enableAsyncActions
848
it('regression: useOptimistic during setState-in-render', async () => {
849
// This is a regression test for a very specific case where useOptimistic is
850
// the first hook in the component, it has a pending update, and a later
@@ -920,7 +882,6 @@ describe('ReactAsyncActions', () => {
882
expect(root).toMatchRenderedOutput('1');
883
});
884
923
- // @gate enableAsyncActions
885
it('useOptimistic accepts a custom reducer', async () => {
886
let serverCart = ['A'];
887
@@ -1052,7 +1013,6 @@ describe('ReactAsyncActions', () => {
1013
);
1014
});
1015
1055
- // @gate enableAsyncActions
1016
it('useOptimistic rebases if the passthrough is updated during a render phase update', async () => {
1017
// This is kind of an esoteric case where it's hard to come up with a
1018
// realistic real-world scenario but it should still work.
@@ -1137,7 +1097,6 @@ describe('ReactAsyncActions', () => {
1097
expect(root).toMatchRenderedOutput(<div>Count: 3</div>);
1098
});
1099
1140
- // @gate enableAsyncActions
1100
it('useOptimistic rebases if the passthrough is updated during a render phase update (initial mount)', async () => {
1101
// This is kind of an esoteric case where it's hard to come up with a
1102
// realistic real-world scenario but it should still work.
@@ -1177,7 +1136,6 @@ describe('ReactAsyncActions', () => {
1136
);
1137
});
1138
1180
- // @gate enableAsyncActions
1139
it('useOptimistic can update repeatedly in the same async action', async () => {
1140
let startTransition;
1141
let setLoadingProgress;
@@ -1241,7 +1199,6 @@ describe('ReactAsyncActions', () => {
1199
expect(root).toMatchRenderedOutput(<div>B</div>);
1200
});
1201
1244
- // @gate enableAsyncActions
1202
it('useOptimistic warns if outside of a transition', async () => {
1203
let startTransition;
1204
let setLoadingProgress;
@@ -1289,7 +1246,6 @@ describe('ReactAsyncActions', () => {
1246
expect(root).toMatchRenderedOutput(<div>B</div>);
1247
});
1248
1292
- // @gate enableAsyncActions
1249
it(
1250
'optimistic state is not reverted until async action finishes, even if ' +
1251
'useTransition hook is unmounted',
@@ -1392,7 +1348,6 @@ describe('ReactAsyncActions', () => {
1348
},
1349
);
1350
1395
- // @gate enableAsyncActions
1351
it(
1352
'updates in an async action are entangled even if useTransition hook ' +
1353
'is unmounted before it finishes',
@@ -1480,7 +1435,6 @@ describe('ReactAsyncActions', () => {
1435
},
1436
);
1437
1483
- // @gate enableAsyncActions
1438
it(
1439
'updates in an async action are entangled even if useTransition hook ' +
1440
'is unmounted before it finishes (class component)',
@@ -1575,7 +1529,6 @@ describe('ReactAsyncActions', () => {
1529
},
1530
);
1531
1578
- // @gate enableAsyncActions
1532
it(
1533
'updates in an async action are entangled even if useTransition hook ' +
1534
'is unmounted before it finishes (root update)',
@@ -1660,7 +1613,6 @@ describe('ReactAsyncActions', () => {
1613
},
1614
);
1615
1663
- // @gate enableAsyncActions
1616
it('React.startTransition supports async actions', async () => {
1617
const startTransition = React.startTransition;
1618
@@ -1698,7 +1650,6 @@ describe('ReactAsyncActions', () => {
1650
expect(root).toMatchRenderedOutput('C');
1651
});
1652
1701
- // @gate enableAsyncActions
1653
it('useOptimistic works with async actions passed to React.startTransition', async () => {
1654
const startTransition = React.startTransition;
1655
@@ -1745,7 +1696,6 @@ describe('ReactAsyncActions', () => {
1696
expect(root).toMatchRenderedOutput(<span>Updated</span>);
1697
});
1698
1748
- // @gate enableAsyncActions
1699
it(
1700
'regression: updates in an action passed to React.startTransition are batched ' +
1701
'even if there were no updates before the first await',
@@ -1816,19 +1766,14 @@ describe('ReactAsyncActions', () => {
1766
);
1767
1768
it('React.startTransition captures async errors and passes them to reportError', async () => {
1819
- // NOTE: This is gated here instead of using the pragma because the failure
1820
- // happens asynchronously and the `gate` runtime doesn't capture it.
1821
- if (gate(flags => flags.enableAsyncActions)) {
1822
- await act(() => {
1823
- React.startTransition(async () => {
1824
- throw new Error('Oops');
1825
- });
1769
+ await act(() => {
1770
+ React.startTransition(async () => {
1771
+ throw new Error('Oops');
1772
});
1827
- assertLog(['reportError: Oops']);
1828
- }
1773
+ });
1774
+ assertLog(['reportError: Oops']);
1775
});
1776
1831
- // @gate enableAsyncActions
1777
it('React.startTransition captures sync errors and passes them to reportError', async () => {
1778
await act(() => {
1779
try {
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMForm-test.js
+1
-9
@@ -361,7 +361,6 @@ describe('ReactFlightDOMForm', () => {
361
expect(foo).toBe('barobject');
362
});
363
364
- // @gate enableAsyncActions
364
it("useActionState's dispatch binds the initial state to the provided action", async () => {
365
const serverAction = serverExports(
366
async function action(prevState, formData) {
@@ -409,7 +408,6 @@ describe('ReactFlightDOMForm', () => {
408
expect(await returnValue).toEqual({count: 6});
409
});
410
412
- // @gate enableAsyncActions
411
it('useActionState can reuse state during MPA form submission', async () => {
412
const serverAction = serverExports(
413
async function action(prevState, formData) {
@@ -498,7 +496,6 @@ describe('ReactFlightDOMForm', () => {
496
}
497
});
498
501
- // @gate enableAsyncActions
499
it(
500
'useActionState preserves state if arity is the same, but different ' +
501
'arguments are bound (i.e. inline closure)',
@@ -617,7 +614,6 @@ describe('ReactFlightDOMForm', () => {
614
},
615
);
616
620
- // @gate enableAsyncActions
617
it('useActionState does not reuse state if action signatures are different', async () => {
618
// This is the same as the previous test, except instead of using bind to
619
// configure the server action (i.e. a closure), it swaps the action.
@@ -704,7 +700,6 @@ describe('ReactFlightDOMForm', () => {
700
expect(container.textContent).toBe('111');
701
});
702
707
- // @gate enableAsyncActions
703
it('when permalink is provided, useActionState compares that instead of the keypath', async () => {
704
const serverAction = serverExports(
705
async function action(prevState, formData) {
@@ -810,7 +805,6 @@ describe('ReactFlightDOMForm', () => {
805
expect(container.textContent).toBe('1');
806
});
807
813
- // @gate enableAsyncActions
808
it('useActionState can change the action URL with the `permalink` argument', async () => {
809
const serverAction = serverExports(function action(prevState) {
810
return {state: prevState.count + 1};
@@ -855,7 +849,6 @@ describe('ReactFlightDOMForm', () => {
849
expect(form.action).toBe('http://localhost/permalink');
850
});
851
858
- // @gate enableAsyncActions
852
it('useActionState `permalink` is coerced to string', async () => {
853
const serverAction = serverExports(function action(prevState) {
854
return {state: prevState.count + 1};
@@ -908,7 +901,6 @@ describe('ReactFlightDOMForm', () => {
901
expect(form.action).toBe('http://localhost/permalink');
902
});
903
911
- // @gate enableAsyncActions
904
it('useActionState can return JSX state during MPA form submission', async () => {
905
const serverAction = serverExports(
906
async function action(prevState, formData) {
@@ -980,7 +972,7 @@ describe('ReactFlightDOMForm', () => {
972
expect(form2.firstChild.tagName).toBe('DIV');
973
});
974
983
- // @gate enableAsyncActions && enableBinaryFlight
975
+ // @gate enableBinaryFlight
976
it('useActionState can return binary state during MPA form submission', async () => {
977
const serverAction = serverExports(
978
async function action(prevState, formData) {
packages/react-server/src/ReactFizzHooks.js
+8
-9
@@ -42,7 +42,6 @@ import {
42
enableCache,
43
enableUseEffectEventHook,
44
enableUseMemoCacheHook,
45
- enableAsyncActions,
45
enableUseResourceEffectHook,
46
} from 'shared/ReactFeatureFlags';
47
import is from 'shared/objectIs';
@@ -833,6 +832,10 @@ export const HooksDispatcher: Dispatcher = supportsClientAPIs
832
useId,
833
// Subscriptions are not setup in a server environment.
834
useSyncExternalStore,
835
+ useOptimistic,
836
+ useActionState,
837
+ useFormState: useActionState,
838
+ useHostTransitionStatus,
839
}
840
: {
841
readContext,
@@ -852,6 +855,10 @@ export const HooksDispatcher: Dispatcher = supportsClientAPIs
855
useTransition: clientHookNotSupported,
856
useId,
857
useSyncExternalStore: clientHookNotSupported,
858
+ useOptimistic,
859
+ useActionState,
860
+ useFormState: useActionState,
861
+ useHostTransitionStatus,
862
};
863
864
if (enableCache) {
@@ -863,14 +870,6 @@ if (enableUseEffectEventHook) {
870
if (enableUseMemoCacheHook) {
871
HooksDispatcher.useMemoCache = useMemoCache;
872
}
866
-if (enableAsyncActions) {
867
- HooksDispatcher.useHostTransitionStatus = useHostTransitionStatus;
868
-}
869
-if (enableAsyncActions) {
870
- HooksDispatcher.useOptimistic = useOptimistic;
871
- HooksDispatcher.useFormState = useActionState;
872
- HooksDispatcher.useActionState = useActionState;
873
-}
873
if (enableUseResourceEffectHook) {
874
HooksDispatcher.useResourceEffect = supportsClientAPIs
875
? noop
packages/react-server/src/ReactFlightHooks.js
+4
@@ -77,6 +77,10 @@ export const HooksDispatcher: Dispatcher = {
77
useImperativeHandle: (unsupportedHook: any),
78
useEffect: (unsupportedHook: any),
79
useId,
80
+ useHostTransitionStatus: (unsupportedHook: any),
81
+ useOptimistic: (unsupportedHook: any),
82
+ useFormState: (unsupportedHook: any),
83
+ useActionState: (unsupportedHook: any),
84
useSyncExternalStore: (unsupportedHook: any),
85
useCacheRefresh(): <T>(?() => T, ?T) => void {
86
return unsupportedRefresh;
packages/react/src/ReactHooks.js
+3
-12
@@ -18,10 +18,7 @@ import {REACT_CONSUMER_TYPE} from 'shared/ReactSymbols';
18
19
import ReactSharedInternals from 'shared/ReactSharedInternals';
20
21
-import {
22
- enableAsyncActions,
23
- enableUseResourceEffectHook,
24
-} from 'shared/ReactFeatureFlags';
21
+import {enableUseResourceEffectHook} from 'shared/ReactFeatureFlags';
22
import {
23
enableContextProfiling,
24
enableLazyContextPropagation,
@@ -255,7 +252,6 @@ export function useOptimistic<S, A>(
252
reducer: ?(S, A) => S,
253
): [S, (A) => void] {
254
const dispatcher = resolveDispatcher();
258
- // $FlowFixMe[not-a-function] This is unstable, thus optional
255
return dispatcher.useOptimistic(passthrough, reducer);
256
}
257
@@ -264,11 +260,6 @@ export function useActionState<S, P>(
260
initialState: Awaited<S>,
261
permalink?: string,
262
): [Awaited<S>, (P) => void, boolean] {
267
- if (!enableAsyncActions) {
268
- throw new Error('Not implemented.');
269
- } else {
270
- const dispatcher = resolveDispatcher();
271
- // $FlowFixMe[not-a-function] This is unstable, thus optional
272
- return dispatcher.useActionState(action, initialState, permalink);
273
- }
263
+ const dispatcher = resolveDispatcher();
264
+ return dispatcher.useActionState(action, initialState, permalink);
265
}
packages/react/src/ReactStartTransition.js
+17
-31
@@ -11,10 +11,7 @@ import type {StartTransitionOptions} from 'shared/ReactTypes';
11
12
import ReactSharedInternals from 'shared/ReactSharedInternals';
13
14
-import {
15
- enableAsyncActions,
16
- enableTransitionTracing,
17
-} from 'shared/ReactFeatureFlags';
14
+import {enableTransitionTracing} from 'shared/ReactFeatureFlags';
15
16
import reportGlobalError from 'shared/reportGlobalError';
17
@@ -37,35 +34,24 @@ export function startTransition(
34
}
35
}
36
40
- if (enableAsyncActions) {
41
- try {
42
- const returnValue = scope();
43
- const onStartTransitionFinish = ReactSharedInternals.S;
44
- if (onStartTransitionFinish !== null) {
45
- onStartTransitionFinish(currentTransition, returnValue);
46
- }
47
- if (
48
- typeof returnValue === 'object' &&
49
- returnValue !== null &&
50
- typeof returnValue.then === 'function'
51
- ) {
52
- returnValue.then(noop, reportGlobalError);
53
- }
54
- } catch (error) {
55
- reportGlobalError(error);
56
- } finally {
57
- warnAboutTransitionSubscriptions(prevTransition, currentTransition);
58
- ReactSharedInternals.T = prevTransition;
37
+ try {
38
+ const returnValue = scope();
39
+ const onStartTransitionFinish = ReactSharedInternals.S;
40
+ if (onStartTransitionFinish !== null) {
41
+ onStartTransitionFinish(currentTransition, returnValue);
42
}
60
- } else {
61
- // When async actions are not enabled, startTransition does not
62
- // capture errors.
63
- try {
64
- scope();
65
- } finally {
66
- warnAboutTransitionSubscriptions(prevTransition, currentTransition);
67
- ReactSharedInternals.T = prevTransition;
43
+ if (
44
+ typeof returnValue === 'object' &&
45
+ returnValue !== null &&
46
+ typeof returnValue.then === 'function'
47
+ ) {
48
+ returnValue.then(noop, reportGlobalError);
49
}
50
+ } catch (error) {
51
+ reportGlobalError(error);
52
+ } finally {
53
+ warnAboutTransitionSubscriptions(prevTransition, currentTransition);
54
+ ReactSharedInternals.T = prevTransition;
55
}
56
}
57
packages/shared/ReactFeatureFlags.js
-1
@@ -31,7 +31,6 @@ export const enableComponentStackLocations = true;
31
32
// TODO: Finish rolling out in www
33
export const favorSafetyOverHydrationPerf = true;
34
-export const enableAsyncActions = true;
34
35
// Need to remove didTimeout argument from Scheduler before landing
36
export const disableSchedulerTimeoutInWorkLoop = false;
packages/shared/forks/ReactFeatureFlags.native-fb.js
-1
@@ -42,7 +42,6 @@ export const disableLegacyContextForFunctionComponents = false;
42
export const disableLegacyMode = false;
43
export const disableSchedulerTimeoutInWorkLoop = false;
44
export const disableTextareaChildren = false;
45
-export const enableAsyncActions = true;
45
export const enableAsyncDebugInfo = false;
46
export const enableAsyncIterableChildren = false;
47
export const enableBinaryFlight = true;
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -29,7 +29,6 @@ export const disableLegacyContextForFunctionComponents = true;
29
export const disableLegacyMode = false;
30
export const disableSchedulerTimeoutInWorkLoop = false;
31
export const disableTextareaChildren = false;
32
-export const enableAsyncActions = true;
32
export const enableAsyncDebugInfo = false;
33
export const enableAsyncIterableChildren = false;
34
export const enableBinaryFlight = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-2
@@ -67,8 +67,6 @@ export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
67
export const enableFizzExternalRuntime = true;
68
export const enableDeferRootSchedulingToMicrotask = true;
69
70
-export const enableAsyncActions = true;
71
-
70
export const alwaysThrottleRetries = true;
71
72
export const passChildrenWhenCloningPersistedNodes = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js
-1
@@ -21,7 +21,6 @@ export const disableLegacyContextForFunctionComponents = false;
21
export const disableLegacyMode = false;
22
export const disableSchedulerTimeoutInWorkLoop = false;
23
export const disableTextareaChildren = false;
24
-export const enableAsyncActions = true;
24
export const enableAsyncDebugInfo = false;
25
export const enableAsyncIterableChildren = false;
26
export const enableBinaryFlight = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-2
@@ -69,8 +69,6 @@ export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
69
export const enableFizzExternalRuntime = false;
70
export const enableDeferRootSchedulingToMicrotask = true;
71
72
-export const enableAsyncActions = true;
73
-
72
export const alwaysThrottleRetries = true;
73
74
export const passChildrenWhenCloningPersistedNodes = false;
packages/shared/forks/ReactFeatureFlags.www.js
-1
@@ -58,7 +58,6 @@ export const enableUseMemoCacheHook = true;
58
export const enableUseEffectEventHook = true;
59
export const enableFilterEmptyStringAttributesDOM = true;
60
export const enableMoveBefore = false;
61
-export const enableAsyncActions = true;
61
export const disableInputAttributeSyncing = false;
62
export const enableLegacyFBSupport = true;
63
export const enableLazyContextPropagation = true;