diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js index 8dbdc9919793a..0d87dbd81aaa6 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js @@ -2672,7 +2672,7 @@ describe('ReactHooksInspectionIntegration', () => { `); }); - // @gate enableFormActions && enableAsyncActions + // @gate enableAsyncActions it('should support useFormState hook', async () => { function Foo() { const [value] = ReactDOM.useFormState(function increment(n) { diff --git a/packages/react-dom-bindings/src/client/ReactDOMComponent.js b/packages/react-dom-bindings/src/client/ReactDOMComponent.js index bd071b4fd17bc..7071f32b75e48 100644 --- a/packages/react-dom-bindings/src/client/ReactDOMComponent.js +++ b/packages/react-dom-bindings/src/client/ReactDOMComponent.js @@ -69,7 +69,6 @@ import { enableBigIntSupport, enableCustomElementPropertySupport, enableClientRenderFallbackOnTextMismatch, - enableFormActions, disableIEWorkarounds, enableTrustedTypesIntegration, enableFilterEmptyStringAttributesDOM, @@ -498,71 +497,54 @@ function setProp( if (__DEV__) { validateFormActionInDevelopment(tag, key, value, props); } - if (enableFormActions) { - if (typeof value === 'function') { - // Set a javascript URL that doesn't do anything. We don't expect this to be invoked - // because we'll preventDefault, but it can happen if a form is manually submitted or - // if someone calls stopPropagation before React gets the event. - // If CSP is used to block javascript: URLs that's fine too. It just won't show this - // error message but the URL will be logged. - domElement.setAttribute( - key, - // eslint-disable-next-line no-script-url - "javascript:throw new Error('" + - 'A React form was unexpectedly submitted. If you called form.submit() manually, ' + - "consider using form.requestSubmit() instead. If you\\'re trying to use " + - 'event.stopPropagation() in a submit event handler, consider also calling ' + - 'event.preventDefault().' + - "')", - ); - break; - } else if (typeof prevValue === 'function') { - // When we're switching off a Server Action that was originally hydrated. - // The server control these fields during SSR that are now trailing. - // The regular diffing doesn't apply since we compare against the previous props. - // Instead, we need to force them to be set to whatever they should be now. - // This would be a lot cleaner if we did this whole fork in the per-tag approach. - if (key === 'formAction') { - if (tag !== 'input') { - // Setting the name here isn't completely safe for inputs if this is switching - // to become a radio button. In that case we let the tag based override take - // control. - setProp(domElement, tag, 'name', props.name, props, null); - } - setProp( - domElement, - tag, - 'formEncType', - props.formEncType, - props, - null, - ); - setProp( - domElement, - tag, - 'formMethod', - props.formMethod, - props, - null, - ); - setProp( - domElement, - tag, - 'formTarget', - props.formTarget, - props, - null, - ); - } else { - setProp(domElement, tag, 'encType', props.encType, props, null); - setProp(domElement, tag, 'method', props.method, props, null); - setProp(domElement, tag, 'target', props.target, props, null); + if (typeof value === 'function') { + // Set a javascript URL that doesn't do anything. We don't expect this to be invoked + // because we'll preventDefault, but it can happen if a form is manually submitted or + // if someone calls stopPropagation before React gets the event. + // If CSP is used to block javascript: URLs that's fine too. It just won't show this + // error message but the URL will be logged. + domElement.setAttribute( + key, + // eslint-disable-next-line no-script-url + "javascript:throw new Error('" + + 'A React form was unexpectedly submitted. If you called form.submit() manually, ' + + "consider using form.requestSubmit() instead. If you\\'re trying to use " + + 'event.stopPropagation() in a submit event handler, consider also calling ' + + 'event.preventDefault().' + + "')", + ); + break; + } else if (typeof prevValue === 'function') { + // When we're switching off a Server Action that was originally hydrated. + // The server control these fields during SSR that are now trailing. + // The regular diffing doesn't apply since we compare against the previous props. + // Instead, we need to force them to be set to whatever they should be now. + // This would be a lot cleaner if we did this whole fork in the per-tag approach. + if (key === 'formAction') { + if (tag !== 'input') { + // Setting the name here isn't completely safe for inputs if this is switching + // to become a radio button. In that case we let the tag based override take + // control. + setProp(domElement, tag, 'name', props.name, props, null); } + setProp( + domElement, + tag, + 'formEncType', + props.formEncType, + props, + null, + ); + setProp(domElement, tag, 'formMethod', props.formMethod, props, null); + setProp(domElement, tag, 'formTarget', props.formTarget, props, null); + } else { + setProp(domElement, tag, 'encType', props.encType, props, null); + setProp(domElement, tag, 'method', props.method, props, null); + setProp(domElement, tag, 'target', props.target, props, null); } } if ( value == null || - (!enableFormActions && typeof value === 'function') || typeof value === 'symbol' || typeof value === 'boolean' ) { @@ -2435,35 +2417,33 @@ function diffHydratedGenericElement( ); continue; case 'action': - case 'formAction': - if (enableFormActions) { - const serverValue = domElement.getAttribute(propKey); - if (typeof value === 'function') { - extraAttributes.delete(propKey.toLowerCase()); - // The server can set these extra properties to implement actions. - // So we remove them from the extra attributes warnings. - if (propKey === 'formAction') { - extraAttributes.delete('name'); - extraAttributes.delete('formenctype'); - extraAttributes.delete('formmethod'); - extraAttributes.delete('formtarget'); - } else { - extraAttributes.delete('enctype'); - extraAttributes.delete('method'); - extraAttributes.delete('target'); - } - // Ideally we should be able to warn if the server value was not a function - // however since the function can return any of these attributes any way it - // wants as a custom progressive enhancement, there's nothing to compare to. - // We can check if the function has the $FORM_ACTION property on the client - // and if it's not, warn, but that's an unnecessary constraint that they - // have to have the extra extension that doesn't do anything on the client. - continue; - } else if (serverValue === EXPECTED_FORM_ACTION_URL) { - extraAttributes.delete(propKey.toLowerCase()); - warnForPropDifference(propKey, 'function', value); - continue; + case 'formAction': { + const serverValue = domElement.getAttribute(propKey); + if (typeof value === 'function') { + extraAttributes.delete(propKey.toLowerCase()); + // The server can set these extra properties to implement actions. + // So we remove them from the extra attributes warnings. + if (propKey === 'formAction') { + extraAttributes.delete('name'); + extraAttributes.delete('formenctype'); + extraAttributes.delete('formmethod'); + extraAttributes.delete('formtarget'); + } else { + extraAttributes.delete('enctype'); + extraAttributes.delete('method'); + extraAttributes.delete('target'); } + // Ideally we should be able to warn if the server value was not a function + // however since the function can return any of these attributes any way it + // wants as a custom progressive enhancement, there's nothing to compare to. + // We can check if the function has the $FORM_ACTION property on the client + // and if it's not, warn, but that's an unnecessary constraint that they + // have to have the extra extension that doesn't do anything on the client. + continue; + } else if (serverValue === EXPECTED_FORM_ACTION_URL) { + extraAttributes.delete(propKey.toLowerCase()); + warnForPropDifference(propKey, 'function', value); + continue; } hydrateSanitizedAttribute( domElement, @@ -2473,6 +2453,7 @@ function diffHydratedGenericElement( extraAttributes, ); continue; + } case 'xlinkHref': hydrateSanitizedAttribute( domElement, diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 213d80c35087d..450038eb70dd9 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -92,7 +92,6 @@ import { enableCreateEventHandleAPI, enableScopeAPI, enableTrustedTypesIntegration, - enableFormActions, enableAsyncActions, } from 'shared/ReactFeatureFlags'; import { @@ -1040,11 +1039,7 @@ export function canHydrateInstance( if (element.nodeName.toLowerCase() !== type.toLowerCase()) { if (!inRootOrSingleton) { // Usually we error for mismatched tags. - if ( - enableFormActions && - element.nodeName === 'INPUT' && - (element: any).type === 'hidden' - ) { + if (element.nodeName === 'INPUT' && (element: any).type === 'hidden') { // If we have extra hidden inputs, we don't mismatch. This allows us to embed // extra form data in the original form. } else { @@ -1054,11 +1049,7 @@ export function canHydrateInstance( // In root or singleton parents we skip past mismatched instances. } else if (!inRootOrSingleton) { // Match - if ( - enableFormActions && - type === 'input' && - (element: any).type === 'hidden' - ) { + if (type === 'input' && (element: any).type === 'hidden') { if (__DEV__) { checkAttributeStringCoercion(anyProps.name, 'name'); } @@ -1190,7 +1181,6 @@ export function canHydrateTextInstance( while (instance.nodeType !== TEXT_NODE) { if ( - enableFormActions && instance.nodeType === ELEMENT_NODE && instance.nodeName === 'INPUT' && (instance: any).type === 'hidden' @@ -1316,8 +1306,7 @@ function getNextHydratable(node: ?Node) { nodeData === SUSPENSE_START_DATA || nodeData === SUSPENSE_FALLBACK_START_DATA || nodeData === SUSPENSE_PENDING_START_DATA || - (enableFormActions && - enableAsyncActions && + (enableAsyncActions && (nodeData === FORM_STATE_IS_MATCHING || nodeData === FORM_STATE_IS_NOT_MATCHING)) ) { @@ -1512,9 +1501,7 @@ export function commitHydratedSuspenseInstance( export function shouldDeleteUnhydratedTailInstances( parentType: string, ): boolean { - return ( - !enableFormActions || (parentType !== 'form' && parentType !== 'button') - ); + return parentType !== 'form' && parentType !== 'button'; } export function didNotMatchHydratedContainerTextInstance( diff --git a/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js b/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js index 6386168268cd7..f26ad485590d6 100644 --- a/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js +++ b/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js @@ -52,7 +52,6 @@ import { enableLegacyFBSupport, enableCreateEventHandleAPI, enableScopeAPI, - enableFormActions, } from 'shared/ReactFeatureFlags'; import {createEventListenerWrapperWithPriority} from './ReactDOMEventListener'; import { @@ -169,17 +168,15 @@ function extractEvents( eventSystemFlags, targetContainer, ); - if (enableFormActions) { - FormActionEventPlugin.extractEvents( - dispatchQueue, - domEventName, - targetInst, - nativeEvent, - nativeEventTarget, - eventSystemFlags, - targetContainer, - ); - } + FormActionEventPlugin.extractEvents( + dispatchQueue, + domEventName, + targetInst, + nativeEvent, + nativeEventTarget, + eventSystemFlags, + targetContainer, + ); } } diff --git a/packages/react-dom-bindings/src/events/ReactDOMEventReplaying.js b/packages/react-dom-bindings/src/events/ReactDOMEventReplaying.js index 59c2920778008..5a3e6919cb364 100644 --- a/packages/react-dom-bindings/src/events/ReactDOMEventReplaying.js +++ b/packages/react-dom-bindings/src/events/ReactDOMEventReplaying.js @@ -46,7 +46,6 @@ import { runWithPriority as attemptHydrationAtPriority, getCurrentUpdatePriority, } from 'react-reconciler/src/ReactEventPriorities'; -import {enableFormActions} from 'shared/ReactFeatureFlags'; // TODO: Upgrade this definition once we're on a newer version of Flow that // has this definition built-in. @@ -535,72 +534,70 @@ export function retryIfBlockedOn( } } - if (enableFormActions) { - // Check the document if there are any queued form actions. - // If there's no ownerDocument, then this is the document. - const root = unblocked.ownerDocument || unblocked; - const formReplayingQueue: void | FormReplayingQueue = (root: any) - .$$reactFormReplay; - if (formReplayingQueue != null) { - for (let i = 0; i < formReplayingQueue.length; i += 3) { - const form: HTMLFormElement = formReplayingQueue[i]; - const submitterOrAction: - | null - | HTMLInputElement - | HTMLButtonElement - | FormAction = formReplayingQueue[i + 1]; - const formProps = getFiberCurrentPropsFromNode(form); - if (typeof submitterOrAction === 'function') { - // This action has already resolved. We're just waiting to dispatch it. - if (!formProps) { - // This was not part of this React instance. It might have been recently - // unblocking us from dispatching our events. So let's make sure we schedule - // a retry. - scheduleReplayQueueIfNeeded(formReplayingQueue); - } - continue; + // Check the document if there are any queued form actions. + // If there's no ownerDocument, then this is the document. + const root = unblocked.ownerDocument || unblocked; + const formReplayingQueue: void | FormReplayingQueue = (root: any) + .$$reactFormReplay; + if (formReplayingQueue != null) { + for (let i = 0; i < formReplayingQueue.length; i += 3) { + const form: HTMLFormElement = formReplayingQueue[i]; + const submitterOrAction: + | null + | HTMLInputElement + | HTMLButtonElement + | FormAction = formReplayingQueue[i + 1]; + const formProps = getFiberCurrentPropsFromNode(form); + if (typeof submitterOrAction === 'function') { + // This action has already resolved. We're just waiting to dispatch it. + if (!formProps) { + // This was not part of this React instance. It might have been recently + // unblocking us from dispatching our events. So let's make sure we schedule + // a retry. + scheduleReplayQueueIfNeeded(formReplayingQueue); } - let target: Node = form; - if (formProps) { - // This form belongs to this React instance but the submitter might - // not be done yet. - let action: null | FormAction = null; - const submitter = submitterOrAction; - if (submitter && submitter.hasAttribute('formAction')) { - // The submitter is the one that is responsible for the action. - target = submitter; - const submitterProps = getFiberCurrentPropsFromNode(submitter); - if (submitterProps) { - // The submitter is part of this instance. - action = (submitterProps: any).formAction; - } else { - const blockedOn = findInstanceBlockingTarget(target); - if (blockedOn !== null) { - // The submitter is not hydrated yet. We'll wait for it. - continue; - } - // The submitter must have been a part of a different React instance. - // Except the form isn't. We don't dispatch actions in this scenario. - } - } else { - action = (formProps: any).action; - } - if (typeof action === 'function') { - formReplayingQueue[i + 1] = action; + continue; + } + let target: Node = form; + if (formProps) { + // This form belongs to this React instance but the submitter might + // not be done yet. + let action: null | FormAction = null; + const submitter = submitterOrAction; + if (submitter && submitter.hasAttribute('formAction')) { + // The submitter is the one that is responsible for the action. + target = submitter; + const submitterProps = getFiberCurrentPropsFromNode(submitter); + if (submitterProps) { + // The submitter is part of this instance. + action = (submitterProps: any).formAction; } else { - // Something went wrong so let's just delete this action. - formReplayingQueue.splice(i, 3); - i -= 3; + const blockedOn = findInstanceBlockingTarget(target); + if (blockedOn !== null) { + // The submitter is not hydrated yet. We'll wait for it. + continue; + } + // The submitter must have been a part of a different React instance. + // Except the form isn't. We don't dispatch actions in this scenario. } - // Schedule a replay in case this unblocked something. - scheduleReplayQueueIfNeeded(formReplayingQueue); - continue; + } else { + action = (formProps: any).action; } - // Something above this target is still blocked so we can't continue yet. - // We're not sure if this target is actually part of this React instance - // yet. It could be a different React as a child but at least some parent is. - // We must continue for any further queued actions. + if (typeof action === 'function') { + formReplayingQueue[i + 1] = action; + } else { + // Something went wrong so let's just delete this action. + formReplayingQueue.splice(i, 3); + i -= 3; + } + // Schedule a replay in case this unblocked something. + scheduleReplayQueueIfNeeded(formReplayingQueue); + continue; } + // Something above this target is still blocked so we can't continue yet. + // We're not sure if this target is actually part of this React instance + // yet. It could be a different React as a child but at least some parent is. + // We must continue for any further queued actions. } } } diff --git a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js index 5e96ca9d6664b..11a707348782b 100644 --- a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js +++ b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js @@ -31,7 +31,6 @@ import { enableBigIntSupport, enableFilterEmptyStringAttributesDOM, enableCustomElementPropertySupport, - enableFormActions, enableFizzExternalRuntime, enableNewBooleanProps, } from 'shared/ReactFeatureFlags'; @@ -1087,7 +1086,7 @@ function pushFormActionAttribute( name: any, ): void | null | FormData { let formData = null; - if (enableFormActions && typeof formAction === 'function') { + if (typeof formAction === 'function') { // Function form actions cannot control the form properties if (__DEV__) { if (name !== null && !didWarnFormActionName) { @@ -1881,7 +1880,7 @@ function pushStartForm( let formData = null; let formActionName = null; - if (enableFormActions && typeof formAction === 'function') { + if (typeof formAction === 'function') { // Function form actions cannot control the form properties if (__DEV__) { if ( diff --git a/packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime.js b/packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime.js index dc5232c8eeda1..419817b52e1f3 100644 --- a/packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime.js +++ b/packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime.js @@ -9,8 +9,6 @@ import { listenToFormSubmissionsForReplaying, } from './ReactDOMFizzInstructionSetShared'; -import {enableFormActions} from 'shared/ReactFeatureFlags'; - export {clientRenderBoundary, completeBoundary, completeSegment}; const resourceMap = new Map(); @@ -140,6 +138,4 @@ export function completeBoundaryWithStyles( ); } -if (enableFormActions) { - listenToFormSubmissionsForReplaying(); -} +listenToFormSubmissionsForReplaying(); diff --git a/packages/react-dom-bindings/src/shared/ReactDOMFormActions.js b/packages/react-dom-bindings/src/shared/ReactDOMFormActions.js index 80d544cd28f44..284eaff1bb6bb 100644 --- a/packages/react-dom-bindings/src/shared/ReactDOMFormActions.js +++ b/packages/react-dom-bindings/src/shared/ReactDOMFormActions.js @@ -10,7 +10,7 @@ import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes'; import type {Awaited} from 'shared/ReactTypes'; -import {enableAsyncActions, enableFormActions} from 'shared/ReactFeatureFlags'; +import {enableAsyncActions} from 'shared/ReactFeatureFlags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; @@ -67,7 +67,7 @@ function resolveDispatcher() { } export function useFormStatus(): FormStatus { - if (!(enableFormActions && enableAsyncActions)) { + if (!enableAsyncActions) { throw new Error('Not implemented.'); } else { const dispatcher = resolveDispatcher(); @@ -81,7 +81,7 @@ export function useFormState( initialState: Awaited, permalink?: string, ): [Awaited, (P) => void, boolean] { - if (!(enableFormActions && enableAsyncActions)) { + if (!enableAsyncActions) { throw new Error('Not implemented.'); } else { const dispatcher = resolveDispatcher(); diff --git a/packages/react-dom-bindings/src/shared/ReactDOMUnknownPropertyHook.js b/packages/react-dom-bindings/src/shared/ReactDOMUnknownPropertyHook.js index 29767d8a65ffb..95551c616d07d 100644 --- a/packages/react-dom-bindings/src/shared/ReactDOMUnknownPropertyHook.js +++ b/packages/react-dom-bindings/src/shared/ReactDOMUnknownPropertyHook.js @@ -11,7 +11,6 @@ import possibleStandardNames from './possibleStandardNames'; import hasOwnProperty from 'shared/hasOwnProperty'; import { enableCustomElementPropertySupport, - enableFormActions, enableNewBooleanProps, } from 'shared/ReactFeatureFlags'; @@ -42,21 +41,18 @@ function validateProperty(tagName, name, value, eventRegistry) { return true; } - if (enableFormActions) { - // Actions are special because unlike events they can have other value types. - if (typeof value === 'function') { - if (tagName === 'form' && name === 'action') { - return true; - } - if (tagName === 'input' && name === 'formAction') { - return true; - } - if (tagName === 'button' && name === 'formAction') { - return true; - } + // Actions are special because unlike events they can have other value types. + if (typeof value === 'function') { + if (tagName === 'form' && name === 'action') { + return true; + } + if (tagName === 'input' && name === 'formAction') { + return true; + } + if (tagName === 'button' && name === 'formAction') { + return true; } } - // We can't rely on the event system being injected on the server. if (eventRegistry != null) { const {registrationNameDependencies, possibleRegistrationNames} = diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js index ad21f230ed380..0c4edff51a490 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js @@ -81,7 +81,6 @@ describe('ReactDOMFizzForm', () => { insertNodesAndExecuteScripts(temp, container, null); } - // @gate enableFormActions it('should allow passing a function to form action during SSR', async () => { const ref = React.createRef(); let foo; @@ -108,7 +107,6 @@ describe('ReactDOMFizzForm', () => { expect(foo).toBe('bar'); }); - // @gate enableFormActions it('should allow passing a function to an input/button formAction', async () => { const inputRef = React.createRef(); const buttonRef = React.createRef(); @@ -167,7 +165,6 @@ describe('ReactDOMFizzForm', () => { expect(rootActionCalled).toBe(false); }); - // @gate enableFormActions || !__DEV__ it('should warn when passing a function action during SSR and string during hydration', async () => { function action(formData) {} function App({isClient}) { @@ -189,7 +186,6 @@ describe('ReactDOMFizzForm', () => { ); }); - // @gate enableFormActions || !__DEV__ it('should ideally warn when passing a string during SSR and function during hydration', async () => { function action(formData) {} function App({isClient}) { @@ -208,7 +204,6 @@ describe('ReactDOMFizzForm', () => { }); }); - // @gate enableFormActions || !__DEV__ it('should reset form fields after you update away from hydrated function', async () => { const formRef = React.createRef(); const inputRef = React.createRef(); @@ -264,7 +259,6 @@ describe('ReactDOMFizzForm', () => { expect(buttonRef.current.hasAttribute('formTarget')).toBe(false); }); - // @gate enableFormActions || !__DEV__ it('should reset form fields after you remove a hydrated function', async () => { const formRef = React.createRef(); const inputRef = React.createRef(); @@ -310,7 +304,6 @@ describe('ReactDOMFizzForm', () => { expect(buttonRef.current.hasAttribute('formTarget')).toBe(false); }); - // @gate enableFormActions || !__DEV__ it('should restore the form fields even if they were incorrectly set', async () => { const formRef = React.createRef(); const inputRef = React.createRef(); @@ -373,7 +366,6 @@ describe('ReactDOMFizzForm', () => { expect(buttonRef.current.hasAttribute('formTarget')).toBe(false); }); - // @gate enableFormActions // @gate enableAsyncActions it('useFormStatus is not pending during server render', async () => { function App() { @@ -389,7 +381,6 @@ describe('ReactDOMFizzForm', () => { expect(container.textContent).toBe('Pending: false'); }); - // @gate enableFormActions it('should replay a form action after hydration', async () => { let foo; function action(formData) { @@ -417,7 +408,6 @@ describe('ReactDOMFizzForm', () => { expect(foo).toBe('bar'); }); - // @gate enableFormActions it('should replay input/button formAction', async () => { let rootActionCalled = false; let savedTitle = null; @@ -477,7 +467,6 @@ describe('ReactDOMFizzForm', () => { expect(container.textContent).toBe('hi'); }); - // @gate enableFormActions // @gate enableAsyncActions it('useActionState returns initial state', async () => { async function action(state) { @@ -499,7 +488,6 @@ describe('ReactDOMFizzForm', () => { expect(container.textContent).toBe('0'); }); - // @gate enableFormActions it('can provide a custom action on the server for actions', async () => { const ref = React.createRef(); let foo; @@ -551,7 +539,6 @@ describe('ReactDOMFizzForm', () => { expect(foo).toBe('bar'); }); - // @gate enableFormActions it('can provide a custom action on buttons the server for actions', async () => { const hiddenRef = React.createRef(); const inputRef = React.createRef(); @@ -631,7 +618,6 @@ describe('ReactDOMFizzForm', () => { expect(foo).toBe('bar'); }); - // @gate enableFormActions it('can hydrate hidden fields in the beginning of a form', async () => { const hiddenRef = React.createRef(); diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 609d24b6fa789..06d0c1b93a307 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -6139,7 +6139,6 @@ describe('ReactDOMFizzServer', () => { expect(getVisibleChildren(container)).toEqual('Hi'); }); - // @gate enableFormActions // @gate enableAsyncActions it('useActionState hydrates without a mismatch', async () => { // This is testing an implementation detail: useActionState emits comment @@ -6193,7 +6192,6 @@ describe('ReactDOMFizzServer', () => { expect(childRef.current).toBe(child); }); - // @gate enableFormActions // @gate enableAsyncActions it("useActionState hydrates without a mismatch if there's a render phase update", async () => { async function action(state) { diff --git a/packages/react-dom/src/__tests__/ReactDOMForm-test.js b/packages/react-dom/src/__tests__/ReactDOMForm-test.js index 677c18425b522..0ebffb2a411a1 100644 --- a/packages/react-dom/src/__tests__/ReactDOMForm-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMForm-test.js @@ -189,7 +189,6 @@ describe('ReactDOMForm', () => { }); } - // @gate enableFormActions it('should allow passing a function to form action', async () => { const ref = React.createRef(); let foo; @@ -230,7 +229,6 @@ describe('ReactDOMForm', () => { expect(foo).toBe('bar2'); }); - // @gate enableFormActions it('should allow passing a function to an input/button formAction', async () => { const inputRef = React.createRef(); const buttonRef = React.createRef(); @@ -323,7 +321,6 @@ describe('ReactDOMForm', () => { expect(rootActionCalled).toBe(false); }); - // @gate enableFormActions || !__DEV__ it('should allow preventing default to block the action', async () => { const ref = React.createRef(); let actionCalled = false; @@ -346,7 +343,6 @@ describe('ReactDOMForm', () => { expect(actionCalled).toBe(false); }); - // @gate enableFormActions it('should only submit the inner of nested forms', async () => { const ref = React.createRef(); let data; @@ -383,7 +379,6 @@ describe('ReactDOMForm', () => { expect(data).toBe('innerinner'); }); - // @gate enableFormActions it('should only submit once if one root is nested inside the other', async () => { const ref = React.createRef(); let outerCalled = 0; @@ -427,7 +422,6 @@ describe('ReactDOMForm', () => { expect(innerCalled).toBe(1); }); - // @gate enableFormActions it('should only submit once if a portal is nested inside its own root', async () => { const ref = React.createRef(); let outerCalled = 0; @@ -471,7 +465,6 @@ describe('ReactDOMForm', () => { expect(innerCalled).toBe(1); }); - // @gate enableFormActions it('can read the clicked button in the formdata event', async () => { const inputRef = React.createRef(); const buttonRef = React.createRef(); @@ -519,7 +512,6 @@ describe('ReactDOMForm', () => { expect(buttonRef.current.getAttribute('type')).toBe(null); }); - // @gate enableFormActions it('excludes the submitter name when the submitter is a function action', async () => { const inputRef = React.createRef(); const buttonRef = React.createRef(); @@ -570,7 +562,6 @@ describe('ReactDOMForm', () => { expect(buttonRef.current.getAttribute('type')).toBe(null); }); - // @gate enableFormActions || !__DEV__ it('allows a non-function formaction to override a function one', async () => { const ref = React.createRef(); let actionCalled = false; @@ -602,7 +593,6 @@ describe('ReactDOMForm', () => { expect(actionCalled).toBe(false); }); - // @gate enableFormActions || !__DEV__ it('allows a non-react html formaction to be invoked', async () => { let actionCalled = false; @@ -638,7 +628,6 @@ describe('ReactDOMForm', () => { expect(actionCalled).toBe(false); }); - // @gate enableFormActions // @gate enableAsyncActions it('form actions are transitions', async () => { const formRef = React.createRef(); @@ -677,7 +666,6 @@ describe('ReactDOMForm', () => { expect(container.textContent).toBe('Updated'); }); - // @gate enableFormActions // @gate enableAsyncActions it('multiple form actions', async () => { const formRef = React.createRef(); @@ -724,7 +712,6 @@ describe('ReactDOMForm', () => { expect(container.textContent).toBe('Count: 2'); }); - // @gate enableFormActions it('form actions can be asynchronous', async () => { const formRef = React.createRef(); @@ -770,7 +757,7 @@ describe('ReactDOMForm', () => { }); it('sync errors in form actions can be captured by an error boundary', async () => { - if (gate(flags => !(flags.enableFormActions && flags.enableAsyncActions))) { + if (gate(flags => !flags.enableAsyncActions)) { // TODO: Uncaught JSDOM errors fail the test after the scope has finished // so don't work with the `gate` mechanism. return; @@ -816,7 +803,7 @@ describe('ReactDOMForm', () => { }); it('async errors in form actions can be captured by an error boundary', async () => { - if (gate(flags => !(flags.enableFormActions && flags.enableAsyncActions))) { + if (gate(flags => !flags.enableAsyncActions)) { // TODO: Uncaught JSDOM errors fail the test after the scope has finished // so don't work with the `gate` mechanism. return; @@ -867,7 +854,6 @@ describe('ReactDOMForm', () => { expect(container.textContent).toBe('Oh no!'); }); - // @gate enableFormActions // @gate enableAsyncActions it('useFormStatus reads the status of a pending form action', async () => { const formRef = React.createRef(); @@ -919,7 +905,6 @@ describe('ReactDOMForm', () => { assertLog(['Async action finished', 'No pending action']); }); - // @gate enableFormActions it('should error if submitting a form manually', async () => { const ref = React.createRef(); @@ -966,7 +951,6 @@ describe('ReactDOMForm', () => { ); }); - // @gate enableFormActions // @gate enableAsyncActions test('useActionState updates state asynchronously and queues multiple actions', async () => { let actionCounter = 0; @@ -1027,7 +1011,6 @@ describe('ReactDOMForm', () => { expect(container.textContent).toBe('2'); }); - // @gate enableFormActions // @gate enableAsyncActions test('useActionState supports inline actions', async () => { let increment; @@ -1060,7 +1043,6 @@ describe('ReactDOMForm', () => { assertLog(['Pending 1', '11']); }); - // @gate enableFormActions // @gate enableAsyncActions test('useActionState: dispatch throws if called during render', async () => { function App() { @@ -1077,7 +1059,6 @@ describe('ReactDOMForm', () => { }); }); - // @gate enableFormActions // @gate enableAsyncActions test('queues multiple actions and runs them in order', async () => { let action; @@ -1110,7 +1091,6 @@ describe('ReactDOMForm', () => { expect(container.textContent).toBe('D'); }); - // @gate enableFormActions // @gate enableAsyncActions test('useActionState: works if action is sync', async () => { let increment; @@ -1143,7 +1123,6 @@ describe('ReactDOMForm', () => { assertLog(['Pending 1', '11']); }); - // @gate enableFormActions // @gate enableAsyncActions test('useActionState: can mix sync and async actions', async () => { let action; @@ -1172,7 +1151,6 @@ describe('ReactDOMForm', () => { expect(container.textContent).toBe('E'); }); - // @gate enableFormActions // @gate enableAsyncActions test('useActionState: error handling (sync action)', async () => { let resetErrorBoundary; @@ -1237,7 +1215,6 @@ describe('ReactDOMForm', () => { expect(container.textContent).toBe('B'); }); - // @gate enableFormActions // @gate enableAsyncActions test('useActionState: error handling (async action)', async () => { let resetErrorBoundary; @@ -1302,7 +1279,6 @@ describe('ReactDOMForm', () => { expect(container.textContent).toBe('B'); }); - // @gate enableFormActions // @gate enableAsyncActions test('useFormState works in StrictMode', async () => { let actionCounter = 0; diff --git a/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js b/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js index 872ef03f38ea6..3ca77a661594b 100644 --- a/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js +++ b/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js @@ -817,7 +817,6 @@ describe('ReactDOMServerHydration', () => { } }); - // @gate enableFormActions it('allows rendering extra hidden inputs in a form', async () => { const element = document.createElement('div'); element.innerHTML = @@ -854,7 +853,6 @@ describe('ReactDOMServerHydration', () => { expect(c.current.value).toBe('C'); }); - // @gate enableFormActions it('allows rendering extra hidden inputs immediately before a text instance', async () => { const element = document.createElement('div'); element.innerHTML = diff --git a/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js b/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js index ec3b05e17ad1e..997f87fa309cd 100644 --- a/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js +++ b/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js @@ -82,7 +82,6 @@ describe('react-dom-server-rendering-stub', () => { expect(x).toBe(false); }); - // @gate enableFormActions // @gate enableAsyncActions it('exports useFormStatus', async () => { function App() { diff --git a/packages/react-dom/src/client/ReactDOMRoot.js b/packages/react-dom/src/client/ReactDOMRoot.js index 563fd6be1fc0c..f963030c91dd5 100644 --- a/packages/react-dom/src/client/ReactDOMRoot.js +++ b/packages/react-dom/src/client/ReactDOMRoot.js @@ -19,7 +19,6 @@ import { allowConcurrentByDefault, disableCommentsAsDOMContainers, enableAsyncActions, - enableFormActions, } from 'shared/ReactFeatureFlags'; export type RootType = { @@ -291,7 +290,7 @@ export function hydrateRoot( if (options.unstable_transitionCallbacks !== undefined) { transitionCallbacks = options.unstable_transitionCallbacks; } - if (enableAsyncActions && enableFormActions) { + if (enableAsyncActions) { if (options.formState !== undefined) { formState = options.formState; } diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index c2d7e061323f5..e754e2aef7713 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -105,7 +105,6 @@ import { enableTransitionTracing, enableLegacyHidden, enableCPUSuspense, - enableFormActions, enableAsyncActions, enablePostpone, enableRenderableContext, @@ -1543,7 +1542,7 @@ function updateHostComponent( workInProgress.flags |= ContentReset; } - if (enableFormActions && enableAsyncActions) { + if (enableAsyncActions) { const memoizedState = workInProgress.memoizedState; if (memoizedState !== null) { // This fiber has been upgraded to a stateful component. The only way diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index dbfb1669aa646..011c9eb504e69 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -41,7 +41,6 @@ import { enableLegacyCache, debugRenderPhaseSideEffectsForStrictMode, enableAsyncActions, - enableFormActions, enableUseDeferredValueInitialArg, } from 'shared/ReactFeatureFlags'; import { @@ -811,7 +810,7 @@ export function renderTransitionAwareHostComponentWithHooks( workInProgress: Fiber, lanes: Lanes, ): TransitionStatus { - if (!(enableFormActions && enableAsyncActions)) { + if (!enableAsyncActions) { throw new Error('Not implemented.'); } return renderWithHooks( @@ -825,7 +824,7 @@ export function renderTransitionAwareHostComponentWithHooks( } export function TransitionAwareHostComponent(): TransitionStatus { - if (!(enableFormActions && enableAsyncActions)) { + if (!enableAsyncActions) { throw new Error('Not implemented.'); } const dispatcher = ReactCurrentDispatcher.current; @@ -2964,11 +2963,6 @@ export function startHostTransition( callback: F => mixed, formData: F, ): void { - if (!enableFormActions) { - // Not implemented. - return; - } - if (!enableAsyncActions) { // Form actions are enabled, but async actions are not. Call the function, // but don't handle any pending or error states. @@ -3089,7 +3083,7 @@ function rerenderTransition(): [ } function useHostTransitionStatus(): TransitionStatus { - if (!(enableFormActions && enableAsyncActions)) { + if (!enableAsyncActions) { throw new Error('Not implemented.'); } const status: TransitionStatus | null = readContext(HostTransitionContext); @@ -3512,7 +3506,7 @@ if (enableUseMemoCacheHook) { if (enableUseEffectEventHook) { (ContextOnlyDispatcher: Dispatcher).useEffectEvent = throwInvalidHookError; } -if (enableFormActions && enableAsyncActions) { +if (enableAsyncActions) { (ContextOnlyDispatcher: Dispatcher).useHostTransitionStatus = throwInvalidHookError; (ContextOnlyDispatcher: Dispatcher).useFormState = throwInvalidHookError; @@ -3551,7 +3545,7 @@ if (enableUseMemoCacheHook) { if (enableUseEffectEventHook) { (HooksDispatcherOnMount: Dispatcher).useEffectEvent = mountEvent; } -if (enableFormActions && enableAsyncActions) { +if (enableAsyncActions) { (HooksDispatcherOnMount: Dispatcher).useHostTransitionStatus = useHostTransitionStatus; (HooksDispatcherOnMount: Dispatcher).useFormState = mountFormState; @@ -3590,7 +3584,7 @@ if (enableUseMemoCacheHook) { if (enableUseEffectEventHook) { (HooksDispatcherOnUpdate: Dispatcher).useEffectEvent = updateEvent; } -if (enableFormActions && enableAsyncActions) { +if (enableAsyncActions) { (HooksDispatcherOnUpdate: Dispatcher).useHostTransitionStatus = useHostTransitionStatus; (HooksDispatcherOnUpdate: Dispatcher).useFormState = updateFormState; @@ -3629,7 +3623,7 @@ if (enableUseMemoCacheHook) { if (enableUseEffectEventHook) { (HooksDispatcherOnRerender: Dispatcher).useEffectEvent = updateEvent; } -if (enableFormActions && enableAsyncActions) { +if (enableAsyncActions) { (HooksDispatcherOnRerender: Dispatcher).useHostTransitionStatus = useHostTransitionStatus; (HooksDispatcherOnRerender: Dispatcher).useFormState = rerenderFormState; @@ -3815,7 +3809,7 @@ if (__DEV__) { return mountEvent(callback); }; } - if (enableFormActions && enableAsyncActions) { + if (enableAsyncActions) { (HooksDispatcherOnMountInDEV: Dispatcher).useHostTransitionStatus = useHostTransitionStatus; (HooksDispatcherOnMountInDEV: Dispatcher).useFormState = @@ -3995,7 +3989,7 @@ if (__DEV__) { return mountEvent(callback); }; } - if (enableFormActions && enableAsyncActions) { + if (enableAsyncActions) { (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useHostTransitionStatus = useHostTransitionStatus; (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useFormState = @@ -4177,7 +4171,7 @@ if (__DEV__) { return updateEvent(callback); }; } - if (enableFormActions && enableAsyncActions) { + if (enableAsyncActions) { (HooksDispatcherOnUpdateInDEV: Dispatcher).useHostTransitionStatus = useHostTransitionStatus; (HooksDispatcherOnUpdateInDEV: Dispatcher).useFormState = @@ -4359,7 +4353,7 @@ if (__DEV__) { return updateEvent(callback); }; } - if (enableFormActions && enableAsyncActions) { + if (enableAsyncActions) { (HooksDispatcherOnRerenderInDEV: Dispatcher).useHostTransitionStatus = useHostTransitionStatus; (HooksDispatcherOnRerenderInDEV: Dispatcher).useFormState = @@ -4562,7 +4556,7 @@ if (__DEV__) { return mountEvent(callback); }; } - if (enableFormActions && enableAsyncActions) { + if (enableAsyncActions) { (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useHostTransitionStatus = useHostTransitionStatus; (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useFormState = @@ -4771,7 +4765,7 @@ if (__DEV__) { return updateEvent(callback); }; } - if (enableFormActions && enableAsyncActions) { + if (enableAsyncActions) { (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useHostTransitionStatus = useHostTransitionStatus; (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useFormState = @@ -4980,7 +4974,7 @@ if (__DEV__) { return updateEvent(callback); }; } - if (enableFormActions && enableAsyncActions) { + if (enableAsyncActions) { (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useHostTransitionStatus = useHostTransitionStatus; (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useFormState = diff --git a/packages/react-reconciler/src/ReactFiberHostContext.js b/packages/react-reconciler/src/ReactFiberHostContext.js index 0b64be766a95b..f930799f56387 100644 --- a/packages/react-reconciler/src/ReactFiberHostContext.js +++ b/packages/react-reconciler/src/ReactFiberHostContext.js @@ -24,7 +24,7 @@ import { } from './ReactFiberConfig'; import {createCursor, push, pop} from './ReactFiberStack'; import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; -import {enableAsyncActions, enableFormActions} from 'shared/ReactFeatureFlags'; +import {enableAsyncActions} from 'shared/ReactFeatureFlags'; const contextStackCursor: StackCursor = createCursor(null); const contextFiberStackCursor: StackCursor = createCursor(null); @@ -110,7 +110,7 @@ function getHostContext(): HostContext { } function pushHostContext(fiber: Fiber): void { - if (enableFormActions && enableAsyncActions) { + if (enableAsyncActions) { const stateHook: Hook | null = fiber.memoizedState; if (stateHook !== null) { // Only provide context if this fiber has been upgraded by a host @@ -139,7 +139,7 @@ function popHostContext(fiber: Fiber): void { pop(contextFiberStackCursor, fiber); } - if (enableFormActions && enableAsyncActions) { + if (enableAsyncActions) { if (hostTransitionProviderCursor.current === fiber) { // Do not pop unless this Fiber provided the current context. This is mostly // a performance optimization, but conveniently it also prevents a potential diff --git a/packages/react-reconciler/src/ReactFiberNewContext.js b/packages/react-reconciler/src/ReactFiberNewContext.js index cb9ada3dee9b8..c19e056b0a3cd 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.js @@ -44,7 +44,6 @@ import {createUpdate, ForceUpdate} from './ReactFiberClassUpdateQueue'; import {markWorkInProgressReceivedUpdate} from './ReactFiberBeginWork'; import { enableLazyContextPropagation, - enableFormActions, enableAsyncActions, enableRenderableContext, } from 'shared/ReactFeatureFlags'; @@ -582,11 +581,7 @@ function propagateParentContextChanges( } } } - } else if ( - enableFormActions && - enableAsyncActions && - parent === getHostTransitionProvider() - ) { + } else if (enableAsyncActions && parent === getHostTransitionProvider()) { // During a host transition, a host component can act like a context // provider. E.g. in React DOM, this would be a
. const currentParent = parent.alternate; diff --git a/packages/react-server-dom-turbopack/src/__tests__/ReactFlightTurbopackDOMForm-test.js b/packages/react-server-dom-turbopack/src/__tests__/ReactFlightTurbopackDOMForm-test.js index 3f63222d0ae2d..e7da697fad8e9 100644 --- a/packages/react-server-dom-turbopack/src/__tests__/ReactFlightTurbopackDOMForm-test.js +++ b/packages/react-server-dom-turbopack/src/__tests__/ReactFlightTurbopackDOMForm-test.js @@ -111,7 +111,6 @@ describe('ReactFlightDOMForm', () => { insertNodesAndExecuteScripts(temp, container, null); } - // @gate enableFormActions it('can submit a passed server action without hydrating it', async () => { let foo = null; diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMForm-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMForm-test.js index e8da630d4ee16..c958aacbea44c 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMForm-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMForm-test.js @@ -133,7 +133,6 @@ describe('ReactFlightDOMForm', () => { insertNodesAndExecuteScripts(temp, container, null); } - // @gate enableFormActions it('can submit a passed server action without hydrating it', async () => { let foo = null; @@ -168,7 +167,6 @@ describe('ReactFlightDOMForm', () => { expect(foo).toBe('bar'); }); - // @gate enableFormActions it('can submit an imported server action without hydrating it', async () => { let foo = null; @@ -201,7 +199,6 @@ describe('ReactFlightDOMForm', () => { expect(foo).toBe('bar'); }); - // @gate enableFormActions it('can submit a complex closure server action without hydrating it', async () => { let foo = null; @@ -236,7 +233,6 @@ describe('ReactFlightDOMForm', () => { expect(foo).toBe('barobject'); }); - // @gate enableFormActions it('can submit a multiple complex closure server action without hydrating it', async () => { let foo = null; @@ -277,7 +273,6 @@ describe('ReactFlightDOMForm', () => { expect(foo).toBe('barc'); }); - // @gate enableFormActions it('can bind an imported server action on the client without hydrating it', async () => { let foo = null; @@ -309,7 +304,6 @@ describe('ReactFlightDOMForm', () => { expect(foo).toBe('barobject'); }); - // @gate enableFormActions it('can bind a server action on the client without hydrating it', async () => { let foo = null; @@ -350,7 +344,6 @@ describe('ReactFlightDOMForm', () => { expect(foo).toBe('barobject'); }); - // @gate enableFormActions // @gate enableAsyncActions it("useActionState's dispatch binds the initial state to the provided action", async () => { const serverAction = serverExports( @@ -399,7 +392,6 @@ describe('ReactFlightDOMForm', () => { expect(await returnValue).toEqual({count: 6}); }); - // @gate enableFormActions // @gate enableAsyncActions it('useActionState can reuse state during MPA form submission', async () => { const serverAction = serverExports( @@ -489,7 +481,6 @@ describe('ReactFlightDOMForm', () => { } }); - // @gate enableFormActions // @gate enableAsyncActions it( 'useActionState preserves state if arity is the same, but different ' + @@ -609,7 +600,6 @@ describe('ReactFlightDOMForm', () => { }, ); - // @gate enableFormActions // @gate enableAsyncActions it('useActionState does not reuse state if action signatures are different', async () => { // This is the same as the previous test, except instead of using bind to @@ -697,7 +687,6 @@ describe('ReactFlightDOMForm', () => { expect(container.textContent).toBe('111'); }); - // @gate enableFormActions // @gate enableAsyncActions it('when permalink is provided, useActionState compares that instead of the keypath', async () => { const serverAction = serverExports( @@ -804,7 +793,6 @@ describe('ReactFlightDOMForm', () => { expect(container.textContent).toBe('1'); }); - // @gate enableFormActions // @gate enableAsyncActions it('useActionState can change the action URL with the `permalink` argument', async () => { const serverAction = serverExports(function action(prevState) { @@ -850,7 +838,6 @@ describe('ReactFlightDOMForm', () => { expect(form.action).toBe('http://localhost/permalink'); }); - // @gate enableFormActions // @gate enableAsyncActions it('useActionState `permalink` is coerced to string', async () => { const serverAction = serverExports(function action(prevState) { @@ -904,7 +891,6 @@ describe('ReactFlightDOMForm', () => { expect(form.action).toBe('http://localhost/permalink'); }); - // @gate enableFormActions // @gate enableAsyncActions it('useFormState can return JSX state during MPA form submission', async () => { const serverAction = serverExports( diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index 1f3e632d31474..3870b699b2e31 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -35,7 +35,6 @@ import { enableUseEffectEventHook, enableUseMemoCacheHook, enableAsyncActions, - enableFormActions, enableUseDeferredValueInitialArg, } from 'shared/ReactFeatureFlags'; import is from 'shared/objectIs'; @@ -814,7 +813,7 @@ if (enableUseEffectEventHook) { if (enableUseMemoCacheHook) { HooksDispatcher.useMemoCache = useMemoCache; } -if (enableFormActions && enableAsyncActions) { +if (enableAsyncActions) { HooksDispatcher.useHostTransitionStatus = useHostTransitionStatus; } if (enableAsyncActions) { diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index 227584698ae94..fada4c9c8c0ad 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -18,7 +18,7 @@ import {REACT_CONSUMER_TYPE} from 'shared/ReactSymbols'; import ReactCurrentDispatcher from './ReactCurrentDispatcher'; import ReactCurrentCache from './ReactCurrentCache'; -import {enableAsyncActions, enableFormActions} from 'shared/ReactFeatureFlags'; +import {enableAsyncActions} from 'shared/ReactFeatureFlags'; type BasicStateAction = (S => S) | S; type Dispatch = A => void; @@ -235,7 +235,7 @@ export function useActionState( initialState: Awaited, permalink?: string, ): [Awaited, (P) => void, boolean] { - if (!(enableFormActions && enableAsyncActions)) { + if (!enableAsyncActions) { throw new Error('Not implemented.'); } else { const dispatcher = resolveDispatcher(); diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index cd6273a229404..0f77667e5bf08 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -31,7 +31,6 @@ export const enableComponentStackLocations = true; // TODO: Finish rolling out in www export const enableClientRenderFallbackOnTextMismatch = true; -export const enableFormActions = true; export const enableAsyncActions = true; // Need to remove didTimeout argument from Scheduler before landing diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 444d956e8dbde..e8fb123513533 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -46,7 +46,6 @@ export const enableCache = true; export const enableLegacyCache = false; export const enableCacheElement = true; export const enableFetchInstrumentation = false; -export const enableFormActions = true; // Doesn't affect Native export const enableBinaryFlight = true; export const enableTaint = true; export const enablePostpone = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 8e992b805f4d9..14ccfc88ef3e1 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -54,7 +54,6 @@ export const enableTaint = __NEXT_RN_MAJOR__; export const enableUnifiedSyncLane = __NEXT_RN_MAJOR__; export const enableFizzExternalRuntime = __NEXT_RN_MAJOR__; // DOM-only export const disableJavaScriptURLs = __NEXT_RN_MAJOR__; // DOM-only -export const enableFormActions = __NEXT_RN_MAJOR__; // DOM-only export const enableBinaryFlight = __NEXT_RN_MAJOR__; // DOM-only export const enableCustomElementPropertySupport = __NEXT_RN_MAJOR__; // DOM-only export const enableServerComponentKeys = __NEXT_RN_MAJOR__; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 819554d5cf6b0..26866114aef3f 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -22,7 +22,6 @@ export const enableCache = true; export const enableLegacyCache = __EXPERIMENTAL__; export const enableCacheElement = __EXPERIMENTAL__; export const enableFetchInstrumentation = true; -export const enableFormActions = true; // Doesn't affect Test Renderer export const enableBinaryFlight = true; export const enableTaint = true; export const enablePostpone = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js index 01fc8ab188614..c9eda11db090c 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js @@ -22,7 +22,6 @@ export const enableCache = true; export const enableLegacyCache = false; export const enableCacheElement = true; export const enableFetchInstrumentation = false; -export const enableFormActions = true; // Doesn't affect Test Renderer export const enableBinaryFlight = true; export const enableTaint = true; export const enablePostpone = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index ba307873cef59..c3ff077cb0860 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -22,7 +22,6 @@ export const enableCache = true; export const enableLegacyCache = true; export const enableCacheElement = true; export const enableFetchInstrumentation = false; -export const enableFormActions = true; // Doesn't affect Test Renderer export const enableBinaryFlight = true; export const enableTaint = true; export const enablePostpone = false; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 698cc86366b2c..d0a2642418b33 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -59,7 +59,6 @@ export const enableCPUSuspense = true; export const enableUseMemoCacheHook = true; export const enableUseEffectEventHook = true; export const enableFilterEmptyStringAttributesDOM = true; -export const enableFormActions = true; export const enableAsyncActions = true; // Logs additional User Timing API marks for use with an experimental profiling tool.