diff --git a/fixtures/dom/src/components/Header.js b/fixtures/dom/src/components/Header.js
index ae24b3223f643..1474d80533657 100644
--- a/fixtures/dom/src/components/Header.js
+++ b/fixtures/dom/src/components/Header.js
@@ -88,6 +88,7 @@ class Header extends React.Component {
+
diff --git a/fixtures/dom/src/components/fixtures/form-actions/index.js b/fixtures/dom/src/components/fixtures/form-actions/index.js
new file mode 100644
index 0000000000000..eace38ba46beb
--- /dev/null
+++ b/fixtures/dom/src/components/fixtures/form-actions/index.js
@@ -0,0 +1,113 @@
+const React = window.React;
+
+const {useState} = React;
+
+async function defer(timeoutMS) {
+ return new Promise(resolve => {
+ setTimeout(resolve, timeoutMS);
+ });
+}
+
+export default function FormActions() {
+ const [textValue, setTextValue] = useState('0');
+ const [radioValue, setRadioValue] = useState('two');
+ const [checkboxValue, setCheckboxValue] = useState([false, true, true]);
+ const [selectValue, setSelectValue] = useState('three');
+
+ return (
+
+ );
+}
diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
index e4c45ccc4c3f1..0a2ba645a2410 100644
--- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
+++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
@@ -6466,5 +6466,7 @@ export const HostTransitionContext: ReactContext = {
export type FormInstance = HTMLFormElement;
export function resetFormInstance(form: FormInstance): void {
+ ReactBrowserEventEmitterSetEnabled(true);
form.reset();
+ ReactBrowserEventEmitterSetEnabled(false);
}
diff --git a/packages/react-dom/src/__tests__/ReactDOMForm-test.js b/packages/react-dom/src/__tests__/ReactDOMForm-test.js
index 96021e305aef2..789c97bbc8318 100644
--- a/packages/react-dom/src/__tests__/ReactDOMForm-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMForm-test.js
@@ -1585,6 +1585,57 @@ describe('ReactDOMForm', () => {
expect(divRef.current.textContent).toEqual('Current username: acdlite');
});
+ it('should fire onReset on automatic form reset', async () => {
+ const formRef = React.createRef();
+ const inputRef = React.createRef();
+
+ let setValue;
+ const defaultValue = 0;
+ function App({promiseForUsername}) {
+ const [value, _setValue] = useState(defaultValue);
+ setValue = _setValue;
+
+ return (
+
+ );
+ }
+
+ const root = ReactDOMClient.createRoot(container);
+ await act(() => root.render());
+
+ // Dirty the controlled input
+ await act(() => setValue('3'));
+ expect(inputRef.current.value).toEqual('3');
+
+ // Submit the form. This will trigger an async action.
+ await submit(formRef.current);
+ assertLog(['Async action started']);
+
+ // We haven't reset yet.
+ expect(inputRef.current.value).toEqual('3');
+
+ // Action completes. onReset has been fired and values reset manually.
+ await act(() => resolveText('Wait'));
+ assertLog([]);
+ expect(inputRef.current.value).toEqual('0');
+ });
+
it('requestFormReset schedules a form reset after transition completes', async () => {
// This is the same as the previous test, except the form is updated with
// a userspace action instead of a built-in form action.