@samitouri / QOS-React-1 / commits / ef63718a27

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;