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.