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 ( +
{ + await defer(500); + }} + onReset={() => { + setTextValue('0'); + setRadioValue('two'); + setCheckboxValue([false, true, true]); + setSelectValue('three'); + }}> +
+
+ type="text" + setTextValue(event.currentTarget.value)} + /> +
+
+ type="radio" + setRadioValue('one')} + /> + setRadioValue('two')} + /> + setRadioValue('three')} + /> +
+
+ type="checkbox" + { + const checked = event.currentTarget.checked; + setCheckboxValue(pending => [checked, pending[1], pending[2]]); + }} + /> + { + const checked = event.currentTarget.checked; + setCheckboxValue(pending => [pending[0], checked, pending[2]]); + }} + /> + { + const checked = event.currentTarget.checked; + setCheckboxValue(pending => [pending[0], pending[1], checked]); + }} + /> +
+
+ select + +
+
+
+ + +
+
+ ); +} 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 ( +
{ + Scheduler.log(`Async action started`); + await getText('Wait'); + }} + onReset={() => { + setValue(defaultValue); + }}> + setValue(event.currentTarget.value)} + /> +
+ ); + } + + 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.