|
13 | 13 | import {FormValidationState} from '@react-stately/form';
|
14 | 14 | import {RefObject, Validation, ValidationResult} from '@react-types/shared';
|
15 | 15 | import {setInteractionModality} from '@react-aria/interactions';
|
16 |
| -import {useEffect} from 'react'; |
| 16 | +import {useEffect, useRef} from 'react'; |
17 | 17 | import {useEffectEvent, useLayoutEffect} from '@react-aria/utils';
|
18 | 18 |
|
19 | 19 | type ValidatableElement = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;
|
@@ -43,8 +43,11 @@ export function useFormValidation<T>(props: FormValidationProps<T>, state: FormV
|
43 | 43 | }
|
44 | 44 | });
|
45 | 45 |
|
| 46 | + let isIgnoredReset = useRef(false); |
46 | 47 | let onReset = useEffectEvent(() => {
|
47 |
| - state.resetValidation(); |
| 48 | + if (!isIgnoredReset.current) { |
| 49 | + state.resetValidation(); |
| 50 | + } |
48 | 51 | });
|
49 | 52 |
|
50 | 53 | let onInvalid = useEffectEvent((e: Event) => {
|
@@ -82,13 +85,32 @@ export function useFormValidation<T>(props: FormValidationProps<T>, state: FormV
|
82 | 85 | }
|
83 | 86 |
|
84 | 87 | let form = input.form;
|
| 88 | + |
| 89 | + let reset = form?.reset; |
| 90 | + if (form) { |
| 91 | + // Try to detect React's automatic form reset behavior so we don't clear |
| 92 | + // validation errors that are returned by server actions. |
| 93 | + // To do this, we ignore programmatic form resets that occur outside a user event. |
| 94 | + // This is best-effort. There may be false positives, e.g. setTimeout. |
| 95 | + form.reset = () => { |
| 96 | + // React uses MessageChannel for scheduling, so ignore 'message' events. |
| 97 | + isIgnoredReset.current = !window.event || (window.event.type === 'message' && window.event.target instanceof MessagePort); |
| 98 | + reset?.call(form); |
| 99 | + isIgnoredReset.current = false; |
| 100 | + }; |
| 101 | + } |
| 102 | + |
85 | 103 | input.addEventListener('invalid', onInvalid);
|
86 | 104 | input.addEventListener('change', onChange);
|
87 | 105 | form?.addEventListener('reset', onReset);
|
88 | 106 | return () => {
|
89 | 107 | input!.removeEventListener('invalid', onInvalid);
|
90 | 108 | input!.removeEventListener('change', onChange);
|
91 | 109 | form?.removeEventListener('reset', onReset);
|
| 110 | + if (form) { |
| 111 | + // @ts-ignore |
| 112 | + form.reset = reset; |
| 113 | + } |
92 | 114 | };
|
93 | 115 | }, [ref, onInvalid, onChange, onReset, validationBehavior]);
|
94 | 116 | }
|
|
0 commit comments