@samitouri / QOS-React-2 / commits / 527ed72bfd

Cleanup enableFormActions flag (#28614)

Cleanup enableFormActions flag

Jan Kassens committed Mar 25, 2024 at 13:25 UTC 527ed72bfd9f5c0045da468ad01db968a9961ad7
30 files changed +184 -310
packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js
+1 -1
@@ -2672,7 +2672,7 @@ describe('ReactHooksInspectionIntegration', () => {
2672 `);
2673 });
2674
2675 - // @gate enableFormActions && enableAsyncActions
2675 + // @gate enableAsyncActions
2676 it('should support useFormState hook', async () => {
2677 function Foo() {
2678 const [value] = ReactDOM.useFormState(function increment(n) {
packages/react-dom-bindings/src/client/ReactDOMComponent.js
+70 -89
@@ -69,7 +69,6 @@ import {
69 enableBigIntSupport,
70 enableCustomElementPropertySupport,
71 enableClientRenderFallbackOnTextMismatch,
72 - enableFormActions,
72 disableIEWorkarounds,
73 enableTrustedTypesIntegration,
74 enableFilterEmptyStringAttributesDOM,
@@ -498,71 +497,54 @@ function setProp(
497 if (__DEV__) {
498 validateFormActionInDevelopment(tag, key, value, props);
499 }
501 - if (enableFormActions) {
502 - if (typeof value === 'function') {
503 - // Set a javascript URL that doesn't do anything. We don't expect this to be invoked
504 - // because we'll preventDefault, but it can happen if a form is manually submitted or
505 - // if someone calls stopPropagation before React gets the event.
506 - // If CSP is used to block javascript: URLs that's fine too. It just won't show this
507 - // error message but the URL will be logged.
508 - domElement.setAttribute(
509 - key,
510 - // eslint-disable-next-line no-script-url
511 - "javascript:throw new Error('" +
512 - 'A React form was unexpectedly submitted. If you called form.submit() manually, ' +
513 - "consider using form.requestSubmit() instead. If you\\'re trying to use " +
514 - 'event.stopPropagation() in a submit event handler, consider also calling ' +
515 - 'event.preventDefault().' +
516 - "')",
517 - );
518 - break;
519 - } else if (typeof prevValue === 'function') {
520 - // When we're switching off a Server Action that was originally hydrated.
521 - // The server control these fields during SSR that are now trailing.
522 - // The regular diffing doesn't apply since we compare against the previous props.
523 - // Instead, we need to force them to be set to whatever they should be now.
524 - // This would be a lot cleaner if we did this whole fork in the per-tag approach.
525 - if (key === 'formAction') {
526 - if (tag !== 'input') {
527 - // Setting the name here isn't completely safe for inputs if this is switching
528 - // to become a radio button. In that case we let the tag based override take
529 - // control.
530 - setProp(domElement, tag, 'name', props.name, props, null);
531 - }
532 - setProp(
533 - domElement,
534 - tag,
535 - 'formEncType',
536 - props.formEncType,
537 - props,
538 - null,
539 - );
540 - setProp(
541 - domElement,
542 - tag,
543 - 'formMethod',
544 - props.formMethod,
545 - props,
546 - null,
547 - );
548 - setProp(
549 - domElement,
550 - tag,
551 - 'formTarget',
552 - props.formTarget,
553 - props,
554 - null,
555 - );
556 - } else {
557 - setProp(domElement, tag, 'encType', props.encType, props, null);
558 - setProp(domElement, tag, 'method', props.method, props, null);
559 - setProp(domElement, tag, 'target', props.target, props, null);
500 + if (typeof value === 'function') {
501 + // Set a javascript URL that doesn't do anything. We don't expect this to be invoked
502 + // because we'll preventDefault, but it can happen if a form is manually submitted or
503 + // if someone calls stopPropagation before React gets the event.
504 + // If CSP is used to block javascript: URLs that's fine too. It just won't show this
505 + // error message but the URL will be logged.
506 + domElement.setAttribute(
507 + key,
508 + // eslint-disable-next-line no-script-url
509 + "javascript:throw new Error('" +
510 + 'A React form was unexpectedly submitted. If you called form.submit() manually, ' +
511 + "consider using form.requestSubmit() instead. If you\\'re trying to use " +
512 + 'event.stopPropagation() in a submit event handler, consider also calling ' +
513 + 'event.preventDefault().' +
514 + "')",
515 + );
516 + break;
517 + } else if (typeof prevValue === 'function') {
518 + // When we're switching off a Server Action that was originally hydrated.
519 + // The server control these fields during SSR that are now trailing.
520 + // The regular diffing doesn't apply since we compare against the previous props.
521 + // Instead, we need to force them to be set to whatever they should be now.
522 + // This would be a lot cleaner if we did this whole fork in the per-tag approach.
523 + if (key === 'formAction') {
524 + if (tag !== 'input') {
525 + // Setting the name here isn't completely safe for inputs if this is switching
526 + // to become a radio button. In that case we let the tag based override take
527 + // control.
528 + setProp(domElement, tag, 'name', props.name, props, null);
529 }
530 + setProp(
531 + domElement,
532 + tag,
533 + 'formEncType',
534 + props.formEncType,
535 + props,
536 + null,
537 + );
538 + setProp(domElement, tag, 'formMethod', props.formMethod, props, null);
539 + setProp(domElement, tag, 'formTarget', props.formTarget, props, null);
540 + } else {
541 + setProp(domElement, tag, 'encType', props.encType, props, null);
542 + setProp(domElement, tag, 'method', props.method, props, null);
543 + setProp(domElement, tag, 'target', props.target, props, null);
544 }
545 }
546 if (
547 value == null ||
565 - (!enableFormActions && typeof value === 'function') ||
548 typeof value === 'symbol' ||
549 typeof value === 'boolean'
550 ) {
@@ -2435,35 +2417,33 @@ function diffHydratedGenericElement(
2417 );
2418 continue;
2419 case 'action':
2438 - case 'formAction':
2439 - if (enableFormActions) {
2440 - const serverValue = domElement.getAttribute(propKey);
2441 - if (typeof value === 'function') {
2442 - extraAttributes.delete(propKey.toLowerCase());
2443 - // The server can set these extra properties to implement actions.
2444 - // So we remove them from the extra attributes warnings.
2445 - if (propKey === 'formAction') {
2446 - extraAttributes.delete('name');
2447 - extraAttributes.delete('formenctype');
2448 - extraAttributes.delete('formmethod');
2449 - extraAttributes.delete('formtarget');
2450 - } else {
2451 - extraAttributes.delete('enctype');
2452 - extraAttributes.delete('method');
2453 - extraAttributes.delete('target');
2454 - }
2455 - // Ideally we should be able to warn if the server value was not a function
2456 - // however since the function can return any of these attributes any way it
2457 - // wants as a custom progressive enhancement, there's nothing to compare to.
2458 - // We can check if the function has the $FORM_ACTION property on the client
2459 - // and if it's not, warn, but that's an unnecessary constraint that they
2460 - // have to have the extra extension that doesn't do anything on the client.
2461 - continue;
2462 - } else if (serverValue === EXPECTED_FORM_ACTION_URL) {
2463 - extraAttributes.delete(propKey.toLowerCase());
2464 - warnForPropDifference(propKey, 'function', value);
2465 - continue;
2420 + case 'formAction': {
2421 + const serverValue = domElement.getAttribute(propKey);
2422 + if (typeof value === 'function') {
2423 + extraAttributes.delete(propKey.toLowerCase());
2424 + // The server can set these extra properties to implement actions.
2425 + // So we remove them from the extra attributes warnings.
2426 + if (propKey === 'formAction') {
2427 + extraAttributes.delete('name');
2428 + extraAttributes.delete('formenctype');
2429 + extraAttributes.delete('formmethod');
2430 + extraAttributes.delete('formtarget');
2431 + } else {
2432 + extraAttributes.delete('enctype');
2433 + extraAttributes.delete('method');
2434 + extraAttributes.delete('target');
2435 }
2436 + // Ideally we should be able to warn if the server value was not a function
2437 + // however since the function can return any of these attributes any way it
2438 + // wants as a custom progressive enhancement, there's nothing to compare to.
2439 + // We can check if the function has the $FORM_ACTION property on the client
2440 + // and if it's not, warn, but that's an unnecessary constraint that they
2441 + // have to have the extra extension that doesn't do anything on the client.
2442 + continue;
2443 + } else if (serverValue === EXPECTED_FORM_ACTION_URL) {
2444 + extraAttributes.delete(propKey.toLowerCase());
2445 + warnForPropDifference(propKey, 'function', value);
2446 + continue;
2447 }
2448 hydrateSanitizedAttribute(
2449 domElement,
@@ -2473,6 +2453,7 @@ function diffHydratedGenericElement(
2453 extraAttributes,
2454 );
2455 continue;
2456 + }
2457 case 'xlinkHref':
2458 hydrateSanitizedAttribute(
2459 domElement,
packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
+4 -17
@@ -92,7 +92,6 @@ import {
92 enableCreateEventHandleAPI,
93 enableScopeAPI,
94 enableTrustedTypesIntegration,
95 - enableFormActions,
95 enableAsyncActions,
96 } from 'shared/ReactFeatureFlags';
97 import {
@@ -1040,11 +1039,7 @@ export function canHydrateInstance(
1039 if (element.nodeName.toLowerCase() !== type.toLowerCase()) {
1040 if (!inRootOrSingleton) {
1041 // Usually we error for mismatched tags.
1043 - if (
1044 - enableFormActions &&
1045 - element.nodeName === 'INPUT' &&
1046 - (element: any).type === 'hidden'
1047 - ) {
1042 + if (element.nodeName === 'INPUT' && (element: any).type === 'hidden') {
1043 // If we have extra hidden inputs, we don't mismatch. This allows us to embed
1044 // extra form data in the original form.
1045 } else {
@@ -1054,11 +1049,7 @@ export function canHydrateInstance(
1049 // In root or singleton parents we skip past mismatched instances.
1050 } else if (!inRootOrSingleton) {
1051 // Match
1057 - if (
1058 - enableFormActions &&
1059 - type === 'input' &&
1060 - (element: any).type === 'hidden'
1061 - ) {
1052 + if (type === 'input' && (element: any).type === 'hidden') {
1053 if (__DEV__) {
1054 checkAttributeStringCoercion(anyProps.name, 'name');
1055 }
@@ -1190,7 +1181,6 @@ export function canHydrateTextInstance(
1181
1182 while (instance.nodeType !== TEXT_NODE) {
1183 if (
1193 - enableFormActions &&
1184 instance.nodeType === ELEMENT_NODE &&
1185 instance.nodeName === 'INPUT' &&
1186 (instance: any).type === 'hidden'
@@ -1316,8 +1306,7 @@ function getNextHydratable(node: ?Node) {
1306 nodeData === SUSPENSE_START_DATA ||
1307 nodeData === SUSPENSE_FALLBACK_START_DATA ||
1308 nodeData === SUSPENSE_PENDING_START_DATA ||
1319 - (enableFormActions &&
1320 - enableAsyncActions &&
1309 + (enableAsyncActions &&
1310 (nodeData === FORM_STATE_IS_MATCHING ||
1311 nodeData === FORM_STATE_IS_NOT_MATCHING))
1312 ) {
@@ -1512,9 +1501,7 @@ export function commitHydratedSuspenseInstance(
1501 export function shouldDeleteUnhydratedTailInstances(
1502 parentType: string,
1503 ): boolean {
1515 - return (
1516 - !enableFormActions || (parentType !== 'form' && parentType !== 'button')
1517 - );
1504 + return parentType !== 'form' && parentType !== 'button';
1505 }
1506
1507 export function didNotMatchHydratedContainerTextInstance(
packages/react-dom-bindings/src/events/DOMPluginEventSystem.js
+9 -12
@@ -52,7 +52,6 @@ import {
52 enableLegacyFBSupport,
53 enableCreateEventHandleAPI,
54 enableScopeAPI,
55 - enableFormActions,
55 } from 'shared/ReactFeatureFlags';
56 import {createEventListenerWrapperWithPriority} from './ReactDOMEventListener';
57 import {
@@ -169,17 +168,15 @@ function extractEvents(
168 eventSystemFlags,
169 targetContainer,
170 );
172 - if (enableFormActions) {
173 - FormActionEventPlugin.extractEvents(
174 - dispatchQueue,
175 - domEventName,
176 - targetInst,
177 - nativeEvent,
178 - nativeEventTarget,
179 - eventSystemFlags,
180 - targetContainer,
181 - );
182 - }
171 + FormActionEventPlugin.extractEvents(
172 + dispatchQueue,
173 + domEventName,
174 + targetInst,
175 + nativeEvent,
176 + nativeEventTarget,
177 + eventSystemFlags,
178 + targetContainer,
179 + );
180 }
181 }
182
packages/react-dom-bindings/src/events/ReactDOMEventReplaying.js
+59 -62
@@ -46,7 +46,6 @@ import {
46 runWithPriority as attemptHydrationAtPriority,
47 getCurrentUpdatePriority,
48 } from 'react-reconciler/src/ReactEventPriorities';
49 -import {enableFormActions} from 'shared/ReactFeatureFlags';
49
50 // TODO: Upgrade this definition once we're on a newer version of Flow that
51 // has this definition built-in.
@@ -535,72 +534,70 @@ export function retryIfBlockedOn(
534 }
535 }
536
538 - if (enableFormActions) {
539 - // Check the document if there are any queued form actions.
540 - // If there's no ownerDocument, then this is the document.
541 - const root = unblocked.ownerDocument || unblocked;
542 - const formReplayingQueue: void | FormReplayingQueue = (root: any)
543 - .$$reactFormReplay;
544 - if (formReplayingQueue != null) {
545 - for (let i = 0; i < formReplayingQueue.length; i += 3) {
546 - const form: HTMLFormElement = formReplayingQueue[i];
547 - const submitterOrAction:
548 - | null
549 - | HTMLInputElement
550 - | HTMLButtonElement
551 - | FormAction = formReplayingQueue[i + 1];
552 - const formProps = getFiberCurrentPropsFromNode(form);
553 - if (typeof submitterOrAction === 'function') {
554 - // This action has already resolved. We're just waiting to dispatch it.
555 - if (!formProps) {
556 - // This was not part of this React instance. It might have been recently
557 - // unblocking us from dispatching our events. So let's make sure we schedule
558 - // a retry.
559 - scheduleReplayQueueIfNeeded(formReplayingQueue);
560 - }
561 - continue;
537 + // Check the document if there are any queued form actions.
538 + // If there's no ownerDocument, then this is the document.
539 + const root = unblocked.ownerDocument || unblocked;
540 + const formReplayingQueue: void | FormReplayingQueue = (root: any)
541 + .$$reactFormReplay;
542 + if (formReplayingQueue != null) {
543 + for (let i = 0; i < formReplayingQueue.length; i += 3) {
544 + const form: HTMLFormElement = formReplayingQueue[i];
545 + const submitterOrAction:
546 + | null
547 + | HTMLInputElement
548 + | HTMLButtonElement
549 + | FormAction = formReplayingQueue[i + 1];
550 + const formProps = getFiberCurrentPropsFromNode(form);
551 + if (typeof submitterOrAction === 'function') {
552 + // This action has already resolved. We're just waiting to dispatch it.
553 + if (!formProps) {
554 + // This was not part of this React instance. It might have been recently
555 + // unblocking us from dispatching our events. So let's make sure we schedule
556 + // a retry.
557 + scheduleReplayQueueIfNeeded(formReplayingQueue);
558 }
563 - let target: Node = form;
564 - if (formProps) {
565 - // This form belongs to this React instance but the submitter might
566 - // not be done yet.
567 - let action: null | FormAction = null;
568 - const submitter = submitterOrAction;
569 - if (submitter && submitter.hasAttribute('formAction')) {
570 - // The submitter is the one that is responsible for the action.
571 - target = submitter;
572 - const submitterProps = getFiberCurrentPropsFromNode(submitter);
573 - if (submitterProps) {
574 - // The submitter is part of this instance.
575 - action = (submitterProps: any).formAction;
576 - } else {
577 - const blockedOn = findInstanceBlockingTarget(target);
578 - if (blockedOn !== null) {
579 - // The submitter is not hydrated yet. We'll wait for it.
580 - continue;
581 - }
582 - // The submitter must have been a part of a different React instance.
583 - // Except the form isn't. We don't dispatch actions in this scenario.
584 - }
585 - } else {
586 - action = (formProps: any).action;
587 - }
588 - if (typeof action === 'function') {
589 - formReplayingQueue[i + 1] = action;
559 + continue;
560 + }
561 + let target: Node = form;
562 + if (formProps) {
563 + // This form belongs to this React instance but the submitter might
564 + // not be done yet.
565 + let action: null | FormAction = null;
566 + const submitter = submitterOrAction;
567 + if (submitter && submitter.hasAttribute('formAction')) {
568 + // The submitter is the one that is responsible for the action.
569 + target = submitter;
570 + const submitterProps = getFiberCurrentPropsFromNode(submitter);
571 + if (submitterProps) {
572 + // The submitter is part of this instance.
573 + action = (submitterProps: any).formAction;
574 } else {
591 - // Something went wrong so let's just delete this action.
592 - formReplayingQueue.splice(i, 3);
593 - i -= 3;
575 + const blockedOn = findInstanceBlockingTarget(target);
576 + if (blockedOn !== null) {
577 + // The submitter is not hydrated yet. We'll wait for it.
578 + continue;
579 + }
580 + // The submitter must have been a part of a different React instance.
581 + // Except the form isn't. We don't dispatch actions in this scenario.
582 }
595 - // Schedule a replay in case this unblocked something.
596 - scheduleReplayQueueIfNeeded(formReplayingQueue);
597 - continue;
583 + } else {
584 + action = (formProps: any).action;
585 }
599 - // Something above this target is still blocked so we can't continue yet.
600 - // We're not sure if this target is actually part of this React instance
601 - // yet. It could be a different React as a child but at least some parent is.
602 - // We must continue for any further queued actions.
586 + if (typeof action === 'function') {
587 + formReplayingQueue[i + 1] = action;
588 + } else {
589 + // Something went wrong so let's just delete this action.
590 + formReplayingQueue.splice(i, 3);
591 + i -= 3;
592 + }
593 + // Schedule a replay in case this unblocked something.
594 + scheduleReplayQueueIfNeeded(formReplayingQueue);
595 + continue;
596 }
597 + // Something above this target is still blocked so we can't continue yet.
598 + // We're not sure if this target is actually part of this React instance
599 + // yet. It could be a different React as a child but at least some parent is.
600 + // We must continue for any further queued actions.
601 }
602 }
603 }
packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
+2 -3
@@ -31,7 +31,6 @@ import {
31 enableBigIntSupport,
32 enableFilterEmptyStringAttributesDOM,
33 enableCustomElementPropertySupport,
34 - enableFormActions,
34 enableFizzExternalRuntime,
35 enableNewBooleanProps,
36 } from 'shared/ReactFeatureFlags';
@@ -1087,7 +1086,7 @@ function pushFormActionAttribute(
1086 name: any,
1087 ): void | null | FormData {
1088 let formData = null;
1090 - if (enableFormActions && typeof formAction === 'function') {
1089 + if (typeof formAction === 'function') {
1090 // Function form actions cannot control the form properties
1091 if (__DEV__) {
1092 if (name !== null && !didWarnFormActionName) {
@@ -1881,7 +1880,7 @@ function pushStartForm(
1880
1881 let formData = null;
1882 let formActionName = null;
1884 - if (enableFormActions && typeof formAction === 'function') {
1883 + if (typeof formAction === 'function') {
1884 // Function form actions cannot control the form properties
1885 if (__DEV__) {
1886 if (
packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime.js
+1 -5
@@ -9,8 +9,6 @@ import {
9 listenToFormSubmissionsForReplaying,
10 } from './ReactDOMFizzInstructionSetShared';
11
12 -import {enableFormActions} from 'shared/ReactFeatureFlags';
13 -
12 export {clientRenderBoundary, completeBoundary, completeSegment};
13
14 const resourceMap = new Map();
@@ -140,6 +138,4 @@ export function completeBoundaryWithStyles(
138 );
139 }
140
143 -if (enableFormActions) {
144 - listenToFormSubmissionsForReplaying();
145 -}
141 +listenToFormSubmissionsForReplaying();
packages/react-dom-bindings/src/shared/ReactDOMFormActions.js
+3 -3
@@ -10,7 +10,7 @@
10 import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
11 import type {Awaited} from 'shared/ReactTypes';
12
13 -import {enableAsyncActions, enableFormActions} from 'shared/ReactFeatureFlags';
13 +import {enableAsyncActions} from 'shared/ReactFeatureFlags';
14 import ReactSharedInternals from 'shared/ReactSharedInternals';
15
16 const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
@@ -67,7 +67,7 @@ function resolveDispatcher() {
67 }
68
69 export function useFormStatus(): FormStatus {
70 - if (!(enableFormActions && enableAsyncActions)) {
70 + if (!enableAsyncActions) {
71 throw new Error('Not implemented.');
72 } else {
73 const dispatcher = resolveDispatcher();
@@ -81,7 +81,7 @@ export function useFormState<S, P>(
81 initialState: Awaited<S>,
82 permalink?: string,
83 ): [Awaited<S>, (P) => void, boolean] {
84 - if (!(enableFormActions && enableAsyncActions)) {
84 + if (!enableAsyncActions) {
85 throw new Error('Not implemented.');
86 } else {
87 const dispatcher = resolveDispatcher();
packages/react-dom-bindings/src/shared/ReactDOMUnknownPropertyHook.js
+10 -14
@@ -11,7 +11,6 @@ import possibleStandardNames from './possibleStandardNames';
11 import hasOwnProperty from 'shared/hasOwnProperty';
12 import {
13 enableCustomElementPropertySupport,
14 - enableFormActions,
14 enableNewBooleanProps,
15 } from 'shared/ReactFeatureFlags';
16
@@ -42,21 +41,18 @@ function validateProperty(tagName, name, value, eventRegistry) {
41 return true;
42 }
43
45 - if (enableFormActions) {
46 - // Actions are special because unlike events they can have other value types.
47 - if (typeof value === 'function') {
48 - if (tagName === 'form' && name === 'action') {
49 - return true;
50 - }
51 - if (tagName === 'input' && name === 'formAction') {
52 - return true;
53 - }
54 - if (tagName === 'button' && name === 'formAction') {
55 - return true;
56 - }
44 + // Actions are special because unlike events they can have other value types.
45 + if (typeof value === 'function') {
46 + if (tagName === 'form' && name === 'action') {
47 + return true;
48 + }
49 + if (tagName === 'input' && name === 'formAction') {
50 + return true;
51 + }
52 + if (tagName === 'button' && name === 'formAction') {
53 + return true;
54 }
55 }
59 -
56 // We can't rely on the event system being injected on the server.
57 if (eventRegistry != null) {
58 const {registrationNameDependencies, possibleRegistrationNames} =
packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js
-14
@@ -81,7 +81,6 @@ describe('ReactDOMFizzForm', () => {
81 insertNodesAndExecuteScripts(temp, container, null);
82 }
83
84 - // @gate enableFormActions
84 it('should allow passing a function to form action during SSR', async () => {
85 const ref = React.createRef();
86 let foo;
@@ -108,7 +107,6 @@ describe('ReactDOMFizzForm', () => {
107 expect(foo).toBe('bar');
108 });
109
111 - // @gate enableFormActions
110 it('should allow passing a function to an input/button formAction', async () => {
111 const inputRef = React.createRef();
112 const buttonRef = React.createRef();
@@ -167,7 +165,6 @@ describe('ReactDOMFizzForm', () => {
165 expect(rootActionCalled).toBe(false);
166 });
167
170 - // @gate enableFormActions || !__DEV__
168 it('should warn when passing a function action during SSR and string during hydration', async () => {
169 function action(formData) {}
170 function App({isClient}) {
@@ -189,7 +186,6 @@ describe('ReactDOMFizzForm', () => {
186 );
187 });
188
192 - // @gate enableFormActions || !__DEV__
189 it('should ideally warn when passing a string during SSR and function during hydration', async () => {
190 function action(formData) {}
191 function App({isClient}) {
@@ -208,7 +204,6 @@ describe('ReactDOMFizzForm', () => {
204 });
205 });
206
211 - // @gate enableFormActions || !__DEV__
207 it('should reset form fields after you update away from hydrated function', async () => {
208 const formRef = React.createRef();
209 const inputRef = React.createRef();
@@ -264,7 +259,6 @@ describe('ReactDOMFizzForm', () => {
259 expect(buttonRef.current.hasAttribute('formTarget')).toBe(false);
260 });
261
267 - // @gate enableFormActions || !__DEV__
262 it('should reset form fields after you remove a hydrated function', async () => {
263 const formRef = React.createRef();
264 const inputRef = React.createRef();
@@ -310,7 +304,6 @@ describe('ReactDOMFizzForm', () => {
304 expect(buttonRef.current.hasAttribute('formTarget')).toBe(false);
305 });
306
313 - // @gate enableFormActions || !__DEV__
307 it('should restore the form fields even if they were incorrectly set', async () => {
308 const formRef = React.createRef();
309 const inputRef = React.createRef();
@@ -373,7 +366,6 @@ describe('ReactDOMFizzForm', () => {
366 expect(buttonRef.current.hasAttribute('formTarget')).toBe(false);
367 });
368
376 - // @gate enableFormActions
369 // @gate enableAsyncActions
370 it('useFormStatus is not pending during server render', async () => {
371 function App() {
@@ -389,7 +381,6 @@ describe('ReactDOMFizzForm', () => {
381 expect(container.textContent).toBe('Pending: false');
382 });
383
392 - // @gate enableFormActions
384 it('should replay a form action after hydration', async () => {
385 let foo;
386 function action(formData) {
@@ -417,7 +408,6 @@ describe('ReactDOMFizzForm', () => {
408 expect(foo).toBe('bar');
409 });
410
420 - // @gate enableFormActions
411 it('should replay input/button formAction', async () => {
412 let rootActionCalled = false;
413 let savedTitle = null;
@@ -477,7 +467,6 @@ describe('ReactDOMFizzForm', () => {
467 expect(container.textContent).toBe('hi');
468 });
469
480 - // @gate enableFormActions
470 // @gate enableAsyncActions
471 it('useActionState returns initial state', async () => {
472 async function action(state) {
@@ -499,7 +488,6 @@ describe('ReactDOMFizzForm', () => {
488 expect(container.textContent).toBe('0');
489 });
490
502 - // @gate enableFormActions
491 it('can provide a custom action on the server for actions', async () => {
492 const ref = React.createRef();
493 let foo;
@@ -551,7 +539,6 @@ describe('ReactDOMFizzForm', () => {
539 expect(foo).toBe('bar');
540 });
541
554 - // @gate enableFormActions
542 it('can provide a custom action on buttons the server for actions', async () => {
543 const hiddenRef = React.createRef();
544 const inputRef = React.createRef();
@@ -631,7 +618,6 @@ describe('ReactDOMFizzForm', () => {
618 expect(foo).toBe('bar');
619 });
620
634 - // @gate enableFormActions
621 it('can hydrate hidden fields in the beginning of a form', async () => {
622 const hiddenRef = React.createRef();
623
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
-2
@@ -6139,7 +6139,6 @@ describe('ReactDOMFizzServer', () => {
6139 expect(getVisibleChildren(container)).toEqual('Hi');
6140 });
6141
6142 - // @gate enableFormActions
6142 // @gate enableAsyncActions
6143 it('useActionState hydrates without a mismatch', async () => {
6144 // This is testing an implementation detail: useActionState emits comment
@@ -6193,7 +6192,6 @@ describe('ReactDOMFizzServer', () => {
6192 expect(childRef.current).toBe(child);
6193 });
6194
6196 - // @gate enableFormActions
6195 // @gate enableAsyncActions
6196 it("useActionState hydrates without a mismatch if there's a render phase update", async () => {
6197 async function action(state) {
packages/react-dom/src/__tests__/ReactDOMForm-test.js
+2 -26
@@ -189,7 +189,6 @@ describe('ReactDOMForm', () => {
189 });
190 }
191
192 - // @gate enableFormActions
192 it('should allow passing a function to form action', async () => {
193 const ref = React.createRef();
194 let foo;
@@ -230,7 +229,6 @@ describe('ReactDOMForm', () => {
229 expect(foo).toBe('bar2');
230 });
231
233 - // @gate enableFormActions
232 it('should allow passing a function to an input/button formAction', async () => {
233 const inputRef = React.createRef();
234 const buttonRef = React.createRef();
@@ -323,7 +321,6 @@ describe('ReactDOMForm', () => {
321 expect(rootActionCalled).toBe(false);
322 });
323
326 - // @gate enableFormActions || !__DEV__
324 it('should allow preventing default to block the action', async () => {
325 const ref = React.createRef();
326 let actionCalled = false;
@@ -346,7 +343,6 @@ describe('ReactDOMForm', () => {
343 expect(actionCalled).toBe(false);
344 });
345
349 - // @gate enableFormActions
346 it('should only submit the inner of nested forms', async () => {
347 const ref = React.createRef();
348 let data;
@@ -383,7 +379,6 @@ describe('ReactDOMForm', () => {
379 expect(data).toBe('innerinner');
380 });
381
386 - // @gate enableFormActions
382 it('should only submit once if one root is nested inside the other', async () => {
383 const ref = React.createRef();
384 let outerCalled = 0;
@@ -427,7 +422,6 @@ describe('ReactDOMForm', () => {
422 expect(innerCalled).toBe(1);
423 });
424
430 - // @gate enableFormActions
425 it('should only submit once if a portal is nested inside its own root', async () => {
426 const ref = React.createRef();
427 let outerCalled = 0;
@@ -471,7 +465,6 @@ describe('ReactDOMForm', () => {
465 expect(innerCalled).toBe(1);
466 });
467
474 - // @gate enableFormActions
468 it('can read the clicked button in the formdata event', async () => {
469 const inputRef = React.createRef();
470 const buttonRef = React.createRef();
@@ -519,7 +512,6 @@ describe('ReactDOMForm', () => {
512 expect(buttonRef.current.getAttribute('type')).toBe(null);
513 });
514
522 - // @gate enableFormActions
515 it('excludes the submitter name when the submitter is a function action', async () => {
516 const inputRef = React.createRef();
517 const buttonRef = React.createRef();
@@ -570,7 +562,6 @@ describe('ReactDOMForm', () => {
562 expect(buttonRef.current.getAttribute('type')).toBe(null);
563 });
564
573 - // @gate enableFormActions || !__DEV__
565 it('allows a non-function formaction to override a function one', async () => {
566 const ref = React.createRef();
567 let actionCalled = false;
@@ -602,7 +593,6 @@ describe('ReactDOMForm', () => {
593 expect(actionCalled).toBe(false);
594 });
595
605 - // @gate enableFormActions || !__DEV__
596 it('allows a non-react html formaction to be invoked', async () => {
597 let actionCalled = false;
598
@@ -638,7 +628,6 @@ describe('ReactDOMForm', () => {
628 expect(actionCalled).toBe(false);
629 });
630
641 - // @gate enableFormActions
631 // @gate enableAsyncActions
632 it('form actions are transitions', async () => {
633 const formRef = React.createRef();
@@ -677,7 +666,6 @@ describe('ReactDOMForm', () => {
666 expect(container.textContent).toBe('Updated');
667 });
668
680 - // @gate enableFormActions
669 // @gate enableAsyncActions
670 it('multiple form actions', async () => {
671 const formRef = React.createRef();
@@ -724,7 +712,6 @@ describe('ReactDOMForm', () => {
712 expect(container.textContent).toBe('Count: 2');
713 });
714
727 - // @gate enableFormActions
715 it('form actions can be asynchronous', async () => {
716 const formRef = React.createRef();
717
@@ -770,7 +757,7 @@ describe('ReactDOMForm', () => {
757 });
758
759 it('sync errors in form actions can be captured by an error boundary', async () => {
773 - if (gate(flags => !(flags.enableFormActions && flags.enableAsyncActions))) {
760 + if (gate(flags => !flags.enableAsyncActions)) {
761 // TODO: Uncaught JSDOM errors fail the test after the scope has finished
762 // so don't work with the `gate` mechanism.
763 return;
@@ -816,7 +803,7 @@ describe('ReactDOMForm', () => {
803 });
804
805 it('async errors in form actions can be captured by an error boundary', async () => {
819 - if (gate(flags => !(flags.enableFormActions && flags.enableAsyncActions))) {
806 + if (gate(flags => !flags.enableAsyncActions)) {
807 // TODO: Uncaught JSDOM errors fail the test after the scope has finished
808 // so don't work with the `gate` mechanism.
809 return;
@@ -867,7 +854,6 @@ describe('ReactDOMForm', () => {
854 expect(container.textContent).toBe('Oh no!');
855 });
856
870 - // @gate enableFormActions
857 // @gate enableAsyncActions
858 it('useFormStatus reads the status of a pending form action', async () => {
859 const formRef = React.createRef();
@@ -919,7 +905,6 @@ describe('ReactDOMForm', () => {
905 assertLog(['Async action finished', 'No pending action']);
906 });
907
922 - // @gate enableFormActions
908 it('should error if submitting a form manually', async () => {
909 const ref = React.createRef();
910
@@ -966,7 +951,6 @@ describe('ReactDOMForm', () => {
951 );
952 });
953
969 - // @gate enableFormActions
954 // @gate enableAsyncActions
955 test('useActionState updates state asynchronously and queues multiple actions', async () => {
956 let actionCounter = 0;
@@ -1027,7 +1011,6 @@ describe('ReactDOMForm', () => {
1011 expect(container.textContent).toBe('2');
1012 });
1013
1030 - // @gate enableFormActions
1014 // @gate enableAsyncActions
1015 test('useActionState supports inline actions', async () => {
1016 let increment;
@@ -1060,7 +1043,6 @@ describe('ReactDOMForm', () => {
1043 assertLog(['Pending 1', '11']);
1044 });
1045
1063 - // @gate enableFormActions
1046 // @gate enableAsyncActions
1047 test('useActionState: dispatch throws if called during render', async () => {
1048 function App() {
@@ -1077,7 +1059,6 @@ describe('ReactDOMForm', () => {
1059 });
1060 });
1061
1080 - // @gate enableFormActions
1062 // @gate enableAsyncActions
1063 test('queues multiple actions and runs them in order', async () => {
1064 let action;
@@ -1110,7 +1091,6 @@ describe('ReactDOMForm', () => {
1091 expect(container.textContent).toBe('D');
1092 });
1093
1113 - // @gate enableFormActions
1094 // @gate enableAsyncActions
1095 test('useActionState: works if action is sync', async () => {
1096 let increment;
@@ -1143,7 +1123,6 @@ describe('ReactDOMForm', () => {
1123 assertLog(['Pending 1', '11']);
1124 });
1125
1146 - // @gate enableFormActions
1126 // @gate enableAsyncActions
1127 test('useActionState: can mix sync and async actions', async () => {
1128 let action;
@@ -1172,7 +1151,6 @@ describe('ReactDOMForm', () => {
1151 expect(container.textContent).toBe('E');
1152 });
1153
1175 - // @gate enableFormActions
1154 // @gate enableAsyncActions
1155 test('useActionState: error handling (sync action)', async () => {
1156 let resetErrorBoundary;
@@ -1237,7 +1215,6 @@ describe('ReactDOMForm', () => {
1215 expect(container.textContent).toBe('B');
1216 });
1217
1240 - // @gate enableFormActions
1218 // @gate enableAsyncActions
1219 test('useActionState: error handling (async action)', async () => {
1220 let resetErrorBoundary;
@@ -1302,7 +1279,6 @@ describe('ReactDOMForm', () => {
1279 expect(container.textContent).toBe('B');
1280 });
1281
1305 - // @gate enableFormActions
1282 // @gate enableAsyncActions
1283 test('useFormState works in StrictMode', async () => {
1284 let actionCounter = 0;
packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js
-2
@@ -817,7 +817,6 @@ describe('ReactDOMServerHydration', () => {
817 }
818 });
819
820 - // @gate enableFormActions
820 it('allows rendering extra hidden inputs in a form', async () => {
821 const element = document.createElement('div');
822 element.innerHTML =
@@ -854,7 +853,6 @@ describe('ReactDOMServerHydration', () => {
853 expect(c.current.value).toBe('C');
854 });
855
857 - // @gate enableFormActions
856 it('allows rendering extra hidden inputs immediately before a text instance', async () => {
857 const element = document.createElement('div');
858 element.innerHTML =
packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js
-1
@@ -82,7 +82,6 @@ describe('react-dom-server-rendering-stub', () => {
82 expect(x).toBe(false);
83 });
84
85 - // @gate enableFormActions
85 // @gate enableAsyncActions
86 it('exports useFormStatus', async () => {
87 function App() {
packages/react-dom/src/client/ReactDOMRoot.js
+1 -2
@@ -19,7 +19,6 @@ import {
19 allowConcurrentByDefault,
20 disableCommentsAsDOMContainers,
21 enableAsyncActions,
22 - enableFormActions,
22 } from 'shared/ReactFeatureFlags';
23
24 export type RootType = {
@@ -291,7 +290,7 @@ export function hydrateRoot(
290 if (options.unstable_transitionCallbacks !== undefined) {
291 transitionCallbacks = options.unstable_transitionCallbacks;
292 }
294 - if (enableAsyncActions && enableFormActions) {
293 + if (enableAsyncActions) {
294 if (options.formState !== undefined) {
295 formState = options.formState;
296 }
packages/react-reconciler/src/ReactFiberBeginWork.js
+1 -2
@@ -105,7 +105,6 @@ import {
105 enableTransitionTracing,
106 enableLegacyHidden,
107 enableCPUSuspense,
108 - enableFormActions,
108 enableAsyncActions,
109 enablePostpone,
110 enableRenderableContext,
@@ -1543,7 +1542,7 @@ function updateHostComponent(
1542 workInProgress.flags |= ContentReset;
1543 }
1544
1546 - if (enableFormActions && enableAsyncActions) {
1545 + if (enableAsyncActions) {
1546 const memoizedState = workInProgress.memoizedState;
1547 if (memoizedState !== null) {
1548 // This fiber has been upgraded to a stateful component. The only way
packages/react-reconciler/src/ReactFiberHooks.js
+14 -20
@@ -41,7 +41,6 @@ import {
41 enableLegacyCache,
42 debugRenderPhaseSideEffectsForStrictMode,
43 enableAsyncActions,
44 - enableFormActions,
44 enableUseDeferredValueInitialArg,
45 } from 'shared/ReactFeatureFlags';
46 import {
@@ -811,7 +810,7 @@ export function renderTransitionAwareHostComponentWithHooks(
810 workInProgress: Fiber,
811 lanes: Lanes,
812 ): TransitionStatus {
814 - if (!(enableFormActions && enableAsyncActions)) {
813 + if (!enableAsyncActions) {
814 throw new Error('Not implemented.');
815 }
816 return renderWithHooks(
@@ -825,7 +824,7 @@ export function renderTransitionAwareHostComponentWithHooks(
824 }
825
826 export function TransitionAwareHostComponent(): TransitionStatus {
828 - if (!(enableFormActions && enableAsyncActions)) {
827 + if (!enableAsyncActions) {
828 throw new Error('Not implemented.');
829 }
830 const dispatcher = ReactCurrentDispatcher.current;
@@ -2964,11 +2963,6 @@ export function startHostTransition<F>(
2963 callback: F => mixed,
2964 formData: F,
2965 ): void {
2967 - if (!enableFormActions) {
2968 - // Not implemented.
2969 - return;
2970 - }
2971 -
2966 if (!enableAsyncActions) {
2967 // Form actions are enabled, but async actions are not. Call the function,
2968 // but don't handle any pending or error states.
@@ -3089,7 +3083,7 @@ function rerenderTransition(): [
3083 }
3084
3085 function useHostTransitionStatus(): TransitionStatus {
3092 - if (!(enableFormActions && enableAsyncActions)) {
3086 + if (!enableAsyncActions) {
3087 throw new Error('Not implemented.');
3088 }
3089 const status: TransitionStatus | null = readContext(HostTransitionContext);
@@ -3512,7 +3506,7 @@ if (enableUseMemoCacheHook) {
3506 if (enableUseEffectEventHook) {
3507 (ContextOnlyDispatcher: Dispatcher).useEffectEvent = throwInvalidHookError;
3508 }
3515 -if (enableFormActions && enableAsyncActions) {
3509 +if (enableAsyncActions) {
3510 (ContextOnlyDispatcher: Dispatcher).useHostTransitionStatus =
3511 throwInvalidHookError;
3512 (ContextOnlyDispatcher: Dispatcher).useFormState = throwInvalidHookError;
@@ -3551,7 +3545,7 @@ if (enableUseMemoCacheHook) {
3545 if (enableUseEffectEventHook) {
3546 (HooksDispatcherOnMount: Dispatcher).useEffectEvent = mountEvent;
3547 }
3554 -if (enableFormActions && enableAsyncActions) {
3548 +if (enableAsyncActions) {
3549 (HooksDispatcherOnMount: Dispatcher).useHostTransitionStatus =
3550 useHostTransitionStatus;
3551 (HooksDispatcherOnMount: Dispatcher).useFormState = mountFormState;
@@ -3590,7 +3584,7 @@ if (enableUseMemoCacheHook) {
3584 if (enableUseEffectEventHook) {
3585 (HooksDispatcherOnUpdate: Dispatcher).useEffectEvent = updateEvent;
3586 }
3593 -if (enableFormActions && enableAsyncActions) {
3587 +if (enableAsyncActions) {
3588 (HooksDispatcherOnUpdate: Dispatcher).useHostTransitionStatus =
3589 useHostTransitionStatus;
3590 (HooksDispatcherOnUpdate: Dispatcher).useFormState = updateFormState;
@@ -3629,7 +3623,7 @@ if (enableUseMemoCacheHook) {
3623 if (enableUseEffectEventHook) {
3624 (HooksDispatcherOnRerender: Dispatcher).useEffectEvent = updateEvent;
3625 }
3632 -if (enableFormActions && enableAsyncActions) {
3626 +if (enableAsyncActions) {
3627 (HooksDispatcherOnRerender: Dispatcher).useHostTransitionStatus =
3628 useHostTransitionStatus;
3629 (HooksDispatcherOnRerender: Dispatcher).useFormState = rerenderFormState;
@@ -3815,7 +3809,7 @@ if (__DEV__) {
3809 return mountEvent(callback);
3810 };
3811 }
3818 - if (enableFormActions && enableAsyncActions) {
3812 + if (enableAsyncActions) {
3813 (HooksDispatcherOnMountInDEV: Dispatcher).useHostTransitionStatus =
3814 useHostTransitionStatus;
3815 (HooksDispatcherOnMountInDEV: Dispatcher).useFormState =
@@ -3995,7 +3989,7 @@ if (__DEV__) {
3989 return mountEvent(callback);
3990 };
3991 }
3998 - if (enableFormActions && enableAsyncActions) {
3992 + if (enableAsyncActions) {
3993 (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useHostTransitionStatus =
3994 useHostTransitionStatus;
3995 (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useFormState =
@@ -4177,7 +4171,7 @@ if (__DEV__) {
4171 return updateEvent(callback);
4172 };
4173 }
4180 - if (enableFormActions && enableAsyncActions) {
4174 + if (enableAsyncActions) {
4175 (HooksDispatcherOnUpdateInDEV: Dispatcher).useHostTransitionStatus =
4176 useHostTransitionStatus;
4177 (HooksDispatcherOnUpdateInDEV: Dispatcher).useFormState =
@@ -4359,7 +4353,7 @@ if (__DEV__) {
4353 return updateEvent(callback);
4354 };
4355 }
4362 - if (enableFormActions && enableAsyncActions) {
4356 + if (enableAsyncActions) {
4357 (HooksDispatcherOnRerenderInDEV: Dispatcher).useHostTransitionStatus =
4358 useHostTransitionStatus;
4359 (HooksDispatcherOnRerenderInDEV: Dispatcher).useFormState =
@@ -4562,7 +4556,7 @@ if (__DEV__) {
4556 return mountEvent(callback);
4557 };
4558 }
4565 - if (enableFormActions && enableAsyncActions) {
4559 + if (enableAsyncActions) {
4560 (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useHostTransitionStatus =
4561 useHostTransitionStatus;
4562 (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useFormState =
@@ -4771,7 +4765,7 @@ if (__DEV__) {
4765 return updateEvent(callback);
4766 };
4767 }
4774 - if (enableFormActions && enableAsyncActions) {
4768 + if (enableAsyncActions) {
4769 (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useHostTransitionStatus =
4770 useHostTransitionStatus;
4771 (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useFormState =
@@ -4980,7 +4974,7 @@ if (__DEV__) {
4974 return updateEvent(callback);
4975 };
4976 }
4983 - if (enableFormActions && enableAsyncActions) {
4977 + if (enableAsyncActions) {
4978 (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useHostTransitionStatus =
4979 useHostTransitionStatus;
4980 (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useFormState =
packages/react-reconciler/src/ReactFiberHostContext.js
+3 -3
@@ -24,7 +24,7 @@ import {
24 } from './ReactFiberConfig';
25 import {createCursor, push, pop} from './ReactFiberStack';
26 import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
27 -import {enableAsyncActions, enableFormActions} from 'shared/ReactFeatureFlags';
27 +import {enableAsyncActions} from 'shared/ReactFeatureFlags';
28
29 const contextStackCursor: StackCursor<HostContext | null> = createCursor(null);
30 const contextFiberStackCursor: StackCursor<Fiber | null> = createCursor(null);
@@ -110,7 +110,7 @@ function getHostContext(): HostContext {
110 }
111
112 function pushHostContext(fiber: Fiber): void {
113 - if (enableFormActions && enableAsyncActions) {
113 + if (enableAsyncActions) {
114 const stateHook: Hook | null = fiber.memoizedState;
115 if (stateHook !== null) {
116 // Only provide context if this fiber has been upgraded by a host
@@ -139,7 +139,7 @@ function popHostContext(fiber: Fiber): void {
139 pop(contextFiberStackCursor, fiber);
140 }
141
142 - if (enableFormActions && enableAsyncActions) {
142 + if (enableAsyncActions) {
143 if (hostTransitionProviderCursor.current === fiber) {
144 // Do not pop unless this Fiber provided the current context. This is mostly
145 // a performance optimization, but conveniently it also prevents a potential
packages/react-reconciler/src/ReactFiberNewContext.js
+1 -6
@@ -44,7 +44,6 @@ import {createUpdate, ForceUpdate} from './ReactFiberClassUpdateQueue';
44 import {markWorkInProgressReceivedUpdate} from './ReactFiberBeginWork';
45 import {
46 enableLazyContextPropagation,
47 - enableFormActions,
47 enableAsyncActions,
48 enableRenderableContext,
49 } from 'shared/ReactFeatureFlags';
@@ -582,11 +581,7 @@ function propagateParentContextChanges(
581 }
582 }
583 }
585 - } else if (
586 - enableFormActions &&
587 - enableAsyncActions &&
588 - parent === getHostTransitionProvider()
589 - ) {
584 + } else if (enableAsyncActions && parent === getHostTransitionProvider()) {
585 // During a host transition, a host component can act like a context
586 // provider. E.g. in React DOM, this would be a <form />.
587 const currentParent = parent.alternate;
packages/react-server-dom-turbopack/src/__tests__/ReactFlightTurbopackDOMForm-test.js
-1
@@ -111,7 +111,6 @@ describe('ReactFlightDOMForm', () => {
111 insertNodesAndExecuteScripts(temp, container, null);
112 }
113
114 - // @gate enableFormActions
114 it('can submit a passed server action without hydrating it', async () => {
115 let foo = null;
116
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMForm-test.js
-14
@@ -133,7 +133,6 @@ describe('ReactFlightDOMForm', () => {
133 insertNodesAndExecuteScripts(temp, container, null);
134 }
135
136 - // @gate enableFormActions
136 it('can submit a passed server action without hydrating it', async () => {
137 let foo = null;
138
@@ -168,7 +167,6 @@ describe('ReactFlightDOMForm', () => {
167 expect(foo).toBe('bar');
168 });
169
171 - // @gate enableFormActions
170 it('can submit an imported server action without hydrating it', async () => {
171 let foo = null;
172
@@ -201,7 +199,6 @@ describe('ReactFlightDOMForm', () => {
199 expect(foo).toBe('bar');
200 });
201
204 - // @gate enableFormActions
202 it('can submit a complex closure server action without hydrating it', async () => {
203 let foo = null;
204
@@ -236,7 +233,6 @@ describe('ReactFlightDOMForm', () => {
233 expect(foo).toBe('barobject');
234 });
235
239 - // @gate enableFormActions
236 it('can submit a multiple complex closure server action without hydrating it', async () => {
237 let foo = null;
238
@@ -277,7 +273,6 @@ describe('ReactFlightDOMForm', () => {
273 expect(foo).toBe('barc');
274 });
275
280 - // @gate enableFormActions
276 it('can bind an imported server action on the client without hydrating it', async () => {
277 let foo = null;
278
@@ -309,7 +304,6 @@ describe('ReactFlightDOMForm', () => {
304 expect(foo).toBe('barobject');
305 });
306
312 - // @gate enableFormActions
307 it('can bind a server action on the client without hydrating it', async () => {
308 let foo = null;
309
@@ -350,7 +344,6 @@ describe('ReactFlightDOMForm', () => {
344 expect(foo).toBe('barobject');
345 });
346
353 - // @gate enableFormActions
347 // @gate enableAsyncActions
348 it("useActionState's dispatch binds the initial state to the provided action", async () => {
349 const serverAction = serverExports(
@@ -399,7 +392,6 @@ describe('ReactFlightDOMForm', () => {
392 expect(await returnValue).toEqual({count: 6});
393 });
394
402 - // @gate enableFormActions
395 // @gate enableAsyncActions
396 it('useActionState can reuse state during MPA form submission', async () => {
397 const serverAction = serverExports(
@@ -489,7 +481,6 @@ describe('ReactFlightDOMForm', () => {
481 }
482 });
483
492 - // @gate enableFormActions
484 // @gate enableAsyncActions
485 it(
486 'useActionState preserves state if arity is the same, but different ' +
@@ -609,7 +600,6 @@ describe('ReactFlightDOMForm', () => {
600 },
601 );
602
612 - // @gate enableFormActions
603 // @gate enableAsyncActions
604 it('useActionState does not reuse state if action signatures are different', async () => {
605 // This is the same as the previous test, except instead of using bind to
@@ -697,7 +687,6 @@ describe('ReactFlightDOMForm', () => {
687 expect(container.textContent).toBe('111');
688 });
689
700 - // @gate enableFormActions
690 // @gate enableAsyncActions
691 it('when permalink is provided, useActionState compares that instead of the keypath', async () => {
692 const serverAction = serverExports(
@@ -804,7 +793,6 @@ describe('ReactFlightDOMForm', () => {
793 expect(container.textContent).toBe('1');
794 });
795
807 - // @gate enableFormActions
796 // @gate enableAsyncActions
797 it('useActionState can change the action URL with the `permalink` argument', async () => {
798 const serverAction = serverExports(function action(prevState) {
@@ -850,7 +838,6 @@ describe('ReactFlightDOMForm', () => {
838 expect(form.action).toBe('http://localhost/permalink');
839 });
840
853 - // @gate enableFormActions
841 // @gate enableAsyncActions
842 it('useActionState `permalink` is coerced to string', async () => {
843 const serverAction = serverExports(function action(prevState) {
@@ -904,7 +891,6 @@ describe('ReactFlightDOMForm', () => {
891 expect(form.action).toBe('http://localhost/permalink');
892 });
893
907 - // @gate enableFormActions
894 // @gate enableAsyncActions
895 it('useFormState can return JSX state during MPA form submission', async () => {
896 const serverAction = serverExports(
packages/react-server/src/ReactFizzHooks.js
+1 -2
@@ -35,7 +35,6 @@ import {
35 enableUseEffectEventHook,
36 enableUseMemoCacheHook,
37 enableAsyncActions,
38 - enableFormActions,
38 enableUseDeferredValueInitialArg,
39 } from 'shared/ReactFeatureFlags';
40 import is from 'shared/objectIs';
@@ -814,7 +813,7 @@ if (enableUseEffectEventHook) {
813 if (enableUseMemoCacheHook) {
814 HooksDispatcher.useMemoCache = useMemoCache;
815 }
817 -if (enableFormActions && enableAsyncActions) {
816 +if (enableAsyncActions) {
817 HooksDispatcher.useHostTransitionStatus = useHostTransitionStatus;
818 }
819 if (enableAsyncActions) {
packages/react/src/ReactHooks.js
+2 -2
@@ -18,7 +18,7 @@ import {REACT_CONSUMER_TYPE} from 'shared/ReactSymbols';
18
19 import ReactCurrentDispatcher from './ReactCurrentDispatcher';
20 import ReactCurrentCache from './ReactCurrentCache';
21 -import {enableAsyncActions, enableFormActions} from 'shared/ReactFeatureFlags';
21 +import {enableAsyncActions} from 'shared/ReactFeatureFlags';
22
23 type BasicStateAction<S> = (S => S) | S;
24 type Dispatch<A> = A => void;
@@ -235,7 +235,7 @@ export function useActionState<S, P>(
235 initialState: Awaited<S>,
236 permalink?: string,
237 ): [Awaited<S>, (P) => void, boolean] {
238 - if (!(enableFormActions && enableAsyncActions)) {
238 + if (!enableAsyncActions) {
239 throw new Error('Not implemented.');
240 } else {
241 const dispatcher = resolveDispatcher();
packages/shared/ReactFeatureFlags.js
-1
@@ -31,7 +31,6 @@ export const enableComponentStackLocations = true;
31
32 // TODO: Finish rolling out in www
33 export const enableClientRenderFallbackOnTextMismatch = true;
34 -export const enableFormActions = true;
34 export const enableAsyncActions = true;
35
36 // Need to remove didTimeout argument from Scheduler before landing
packages/shared/forks/ReactFeatureFlags.native-fb.js
-1
@@ -46,7 +46,6 @@ export const enableCache = true;
46 export const enableLegacyCache = false;
47 export const enableCacheElement = true;
48 export const enableFetchInstrumentation = false;
49 -export const enableFormActions = true; // Doesn't affect Native
49 export const enableBinaryFlight = true;
50 export const enableTaint = true;
51 export const enablePostpone = false;
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -54,7 +54,6 @@ export const enableTaint = __NEXT_RN_MAJOR__;
54 export const enableUnifiedSyncLane = __NEXT_RN_MAJOR__;
55 export const enableFizzExternalRuntime = __NEXT_RN_MAJOR__; // DOM-only
56 export const disableJavaScriptURLs = __NEXT_RN_MAJOR__; // DOM-only
57 -export const enableFormActions = __NEXT_RN_MAJOR__; // DOM-only
57 export const enableBinaryFlight = __NEXT_RN_MAJOR__; // DOM-only
58 export const enableCustomElementPropertySupport = __NEXT_RN_MAJOR__; // DOM-only
59 export const enableServerComponentKeys = __NEXT_RN_MAJOR__;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-1
@@ -22,7 +22,6 @@ export const enableCache = true;
22 export const enableLegacyCache = __EXPERIMENTAL__;
23 export const enableCacheElement = __EXPERIMENTAL__;
24 export const enableFetchInstrumentation = true;
25 -export const enableFormActions = true; // Doesn't affect Test Renderer
25 export const enableBinaryFlight = true;
26 export const enableTaint = true;
27 export const enablePostpone = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
-1
@@ -22,7 +22,6 @@ export const enableCache = true;
22 export const enableLegacyCache = false;
23 export const enableCacheElement = true;
24 export const enableFetchInstrumentation = false;
25 -export const enableFormActions = true; // Doesn't affect Test Renderer
25 export const enableBinaryFlight = true;
26 export const enableTaint = true;
27 export const enablePostpone = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-1
@@ -22,7 +22,6 @@ export const enableCache = true;
22 export const enableLegacyCache = true;
23 export const enableCacheElement = true;
24 export const enableFetchInstrumentation = false;
25 -export const enableFormActions = true; // Doesn't affect Test Renderer
25 export const enableBinaryFlight = true;
26 export const enableTaint = true;
27 export const enablePostpone = false;
packages/shared/forks/ReactFeatureFlags.www.js
-1
@@ -59,7 +59,6 @@ export const enableCPUSuspense = true;
59 export const enableUseMemoCacheHook = true;
60 export const enableUseEffectEventHook = true;
61 export const enableFilterEmptyStringAttributesDOM = true;
62 -export const enableFormActions = true;
62 export const enableAsyncActions = true;
63
64 // Logs additional User Timing API marks for use with an experimental profiling tool.