diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index ac76fafd5ad86..9e937c013829c 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -102,6 +102,7 @@ import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem'; import {validateLinkPropsForStyleResource} from '../shared/ReactDOMResourceValidation'; import escapeSelectorAttributeValueInsideDoubleQuotes from './escapeSelectorAttributeValueInsideDoubleQuotes'; import {flushSyncWork as flushSyncWorkOnAllRoots} from 'react-reconciler/src/ReactFiberWorkLoop'; +import {requestFormReset as requestFormResetOnFiber} from 'react-reconciler/src/ReactFiberHooks'; import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals'; @@ -1928,6 +1929,7 @@ ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */ = { f /* flushSyncWork */: disableLegacyMode ? flushSyncWork : previousDispatcher.f /* flushSyncWork */, + r: requestFormReset, D /* prefetchDNS */: prefetchDNS, C /* preconnect */: preconnect, L /* preload */: preload, @@ -1951,6 +1953,23 @@ function flushSyncWork() { } } +function requestFormReset(form: HTMLFormElement) { + const formInst = getInstanceFromNodeDOMTree(form); + if ( + formInst !== null && + formInst.tag === HostComponent && + formInst.type === 'form' + ) { + requestFormResetOnFiber(formInst); + } else { + // This form was either not rendered by this React renderer (or it's an + // invalid type). Try the next one. + // + // The last implementation in the sequence will throw an error. + previousDispatcher.r(/* requestFormReset */ form); + } +} + // We expect this to get inlined. It is a function mostly to communicate the special nature of // how we resolve the HoistableRoot for ReactDOM.pre*() methods. Because we support calling // these methods outside of render there is no way to know which Document or ShadowRoot is 'scoped' diff --git a/packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js b/packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js index 602452f67e2f3..3303c07cfb4f3 100644 --- a/packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js +++ b/packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js @@ -28,6 +28,7 @@ const previousDispatcher = ReactDOMSharedInternals.d; /* ReactDOMCurrentDispatcher */ ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */ = { f /* flushSyncWork */: previousDispatcher.f /* flushSyncWork */, + r /* requestFormReset */: previousDispatcher.r /* requestFormReset */, D /* prefetchDNS */: prefetchDNS, C /* preconnect */: preconnect, L /* preload */: preload, diff --git a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js index f8989c18a4a8a..73295820485e7 100644 --- a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js +++ b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js @@ -88,6 +88,7 @@ const previousDispatcher = ReactDOMSharedInternals.d; /* ReactDOMCurrentDispatcher */ ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */ = { f /* flushSyncWork */: previousDispatcher.f /* flushSyncWork */, + r /* requestFormReset */: previousDispatcher.r /* requestFormReset */, D /* prefetchDNS */: prefetchDNS, C /* preconnect */: preconnect, L /* preload */: preload, diff --git a/packages/react-dom-bindings/src/shared/ReactDOMFormActions.js b/packages/react-dom-bindings/src/shared/ReactDOMFormActions.js index 42967f7ce499b..0b3f478e33b19 100644 --- a/packages/react-dom-bindings/src/shared/ReactDOMFormActions.js +++ b/packages/react-dom-bindings/src/shared/ReactDOMFormActions.js @@ -12,6 +12,7 @@ import type {Awaited} from 'shared/ReactTypes'; import {enableAsyncActions} from 'shared/ReactFeatureFlags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; +import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals'; type FormStatusNotPending = {| pending: false, @@ -87,3 +88,8 @@ export function useFormState( return dispatcher.useFormState(action, initialState, permalink); } } + +export function requestFormReset(form: HTMLFormElement) { + ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */ + .r(/* requestFormReset */ form); +} diff --git a/packages/react-dom/index.classic.fb.js b/packages/react-dom/index.classic.fb.js index a565a91f43b1f..63c20f7e51bfe 100644 --- a/packages/react-dom/index.classic.fb.js +++ b/packages/react-dom/index.classic.fb.js @@ -25,6 +25,7 @@ export { unstable_runWithPriority, // DO NOT USE: Temporarily exposed to migrate off of Scheduler.runWithPriority. useFormStatus, useFormState, + requestFormReset, prefetchDNS, preconnect, preload, diff --git a/packages/react-dom/index.experimental.js b/packages/react-dom/index.experimental.js index f2d3c27aea0c5..0cc5ef13e3698 100644 --- a/packages/react-dom/index.experimental.js +++ b/packages/react-dom/index.experimental.js @@ -17,6 +17,7 @@ export { unstable_runWithPriority, // DO NOT USE: Temporarily exposed to migrate off of Scheduler.runWithPriority. useFormStatus, useFormState, + requestFormReset, prefetchDNS, preconnect, preload, diff --git a/packages/react-dom/index.js b/packages/react-dom/index.js index 50ad087dfaf6c..b6f85ce628c57 100644 --- a/packages/react-dom/index.js +++ b/packages/react-dom/index.js @@ -20,6 +20,7 @@ export { unstable_runWithPriority, // DO NOT USE: Temporarily exposed to migrate off of Scheduler.runWithPriority. useFormStatus, useFormState, + requestFormReset, prefetchDNS, preconnect, preload, diff --git a/packages/react-dom/index.modern.fb.js b/packages/react-dom/index.modern.fb.js index 0d2fa4b4dccbd..861febfa5e37b 100644 --- a/packages/react-dom/index.modern.fb.js +++ b/packages/react-dom/index.modern.fb.js @@ -16,6 +16,7 @@ export { unstable_runWithPriority, // DO NOT USE: Temporarily exposed to migrate off of Scheduler.runWithPriority. useFormStatus, useFormState, + requestFormReset, prefetchDNS, preconnect, preload, diff --git a/packages/react-dom/index.stable.js b/packages/react-dom/index.stable.js index a9306083c0e61..f5ed138d67ab6 100644 --- a/packages/react-dom/index.stable.js +++ b/packages/react-dom/index.stable.js @@ -16,6 +16,7 @@ export { unstable_batchedUpdates, useFormStatus, useFormState, + requestFormReset, prefetchDNS, preconnect, preload, diff --git a/packages/react-dom/src/ReactDOMSharedInternals.js b/packages/react-dom/src/ReactDOMSharedInternals.js index 50f19f563c9ad..0a0f97866738a 100644 --- a/packages/react-dom/src/ReactDOMSharedInternals.js +++ b/packages/react-dom/src/ReactDOMSharedInternals.js @@ -29,8 +29,16 @@ export type ReactDOMInternalsDev = ReactDOMInternals & { function noop() {} +function requestFormReset(element: HTMLFormElement) { + throw new Error( + 'Invalid form element. requestFormReset must be passed a form that was ' + + 'rendered by React.', + ); +} + const DefaultDispatcher: HostDispatcher = { f /* flushSyncWork */: noop, + r /* requestFormReset */: requestFormReset, D /* prefetchDNS */: noop, C /* preconnect */: noop, L /* preload */: noop, diff --git a/packages/react-dom/src/ReactDOMSharedInternalsFB.js b/packages/react-dom/src/ReactDOMSharedInternalsFB.js index 7bd7a1ab8ae2b..4e425d5ddca79 100644 --- a/packages/react-dom/src/ReactDOMSharedInternalsFB.js +++ b/packages/react-dom/src/ReactDOMSharedInternalsFB.js @@ -27,6 +27,7 @@ function noop() {} const DefaultDispatcher: HostDispatcher = { f /* flushSyncWork */: noop, + r /* requestFormReset */: noop, D /* prefetchDNS */: noop, C /* preconnect */: noop, L /* preload */: noop, diff --git a/packages/react-dom/src/client/ReactDOM.js b/packages/react-dom/src/client/ReactDOM.js index 86979fa11f81d..61660e9843369 100644 --- a/packages/react-dom/src/client/ReactDOM.js +++ b/packages/react-dom/src/client/ReactDOM.js @@ -48,6 +48,7 @@ export { export { useFormStatus, useFormState, + requestFormReset, } from 'react-dom-bindings/src/shared/ReactDOMFormActions'; if (__DEV__) { diff --git a/packages/react-dom/src/client/ReactDOMFB.js b/packages/react-dom/src/client/ReactDOMFB.js index e24cba3a52b46..9b016b04d8dfd 100644 --- a/packages/react-dom/src/client/ReactDOMFB.js +++ b/packages/react-dom/src/client/ReactDOMFB.js @@ -48,6 +48,7 @@ export { export { useFormStatus, useFormState, + requestFormReset, } from 'react-dom-bindings/src/shared/ReactDOMFormActions'; if (__DEV__) { diff --git a/packages/react-dom/src/shared/ReactDOMTypes.js b/packages/react-dom/src/shared/ReactDOMTypes.js index 2da68cae2d3f3..dbfe07f9ec8f3 100644 --- a/packages/react-dom/src/shared/ReactDOMTypes.js +++ b/packages/react-dom/src/shared/ReactDOMTypes.js @@ -83,6 +83,7 @@ export type PreinitModuleScriptOptions = { export type HostDispatcher = { f /* flushSyncWork */: () => boolean | void, + r /* requestFormReset */: (form: HTMLFormElement) => void, D /* prefetchDNS */: (href: string) => void, C /* preconnect */: (href: string, crossOrigin?: ?CrossOriginEnum) => void, L /* preload */: ( diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index b32c9c120166d..c712eb6c387d8 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -3008,13 +3008,18 @@ export function startHostTransition( // once more of this function is implemented. () => { // Automatically reset the form when the action completes. - requestFormReset(formFiber); + requestFormResetImpl(formFiber); return callback(formData); }, ); } -function requestFormReset(formFiber: Fiber) { +export function requestFormReset(formFiber: Fiber) { + // TODO: Not yet implemented. Need to upgrade the fiber to be stateful + // before scheduling the form reset. +} + +function requestFormResetImpl(formFiber: Fiber) { const transition = requestCurrentTransition(); if (__DEV__) { diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 799ce858f3e19..2ccd0ee9f9a3e 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -506,5 +506,6 @@ "518": "Saw multiple hydration diff roots in a pass. This is a bug in React.", "519": "Hydration Mismatch Exception: This is not a real error, and should not leak into userspace. If you're seeing this, it's likely a bug in React.", "520": "There was an error during concurrent rendering but React was able to recover by instead synchronously rendering the entire root.", - "521": "flushSyncWork should not be called from builds that support legacy mode. This is a bug in React." + "521": "flushSyncWork should not be called from builds that support legacy mode. This is a bug in React.", + "522": "Invalid form element. requestFormReset must be passed a form that was rendered by React." }