react-hook-form 7.87.0 → 7.89.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/controller.d.ts +7 -35
- package/dist/controller.d.ts.map +1 -1
- package/dist/errorMessage.d.ts +27 -0
- package/dist/errorMessage.d.ts.map +1 -0
- package/dist/form.d.ts +7 -15
- package/dist/form.d.ts.map +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.mjs +390 -322
- package/dist/index.esm.mjs.map +1 -1
- package/dist/index.react-server.d.ts +3 -0
- package/dist/index.react-server.d.ts.map +1 -0
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/logic/createFormControl.d.ts.map +1 -1
- package/dist/logic/getResolverOptions.d.ts +1 -0
- package/dist/logic/getResolverOptions.d.ts.map +1 -1
- package/dist/logic/iterateFieldsByAction.d.ts +1 -1
- package/dist/logic/iterateFieldsByAction.d.ts.map +1 -1
- package/dist/logic/schemaErrorLookup.d.ts.map +1 -1
- package/dist/logic/validateField.d.ts.map +1 -1
- package/dist/react-server.esm.mjs +2535 -0
- package/dist/react-server.esm.mjs.map +1 -0
- package/dist/types/fields.d.ts +1 -0
- package/dist/types/fields.d.ts.map +1 -1
- package/dist/useController.d.ts +5 -17
- package/dist/useController.d.ts.map +1 -1
- package/dist/useFieldArray.d.ts +5 -30
- package/dist/useFieldArray.d.ts.map +1 -1
- package/dist/useForm.d.ts +7 -22
- package/dist/useForm.d.ts.map +1 -1
- package/dist/useFormContext.d.ts +10 -46
- package/dist/useFormContext.d.ts.map +1 -1
- package/dist/useFormState.d.ts +4 -23
- package/dist/useFormState.d.ts.map +1 -1
- package/dist/useWatch.d.ts +7 -140
- package/dist/useWatch.d.ts.map +1 -1
- package/dist/utils/cloneObject.d.ts.map +1 -1
- package/dist/utils/flatten.d.ts.map +1 -1
- package/dist/utils/formData.d.ts.map +1 -1
- package/dist/watch.d.ts +4 -16
- package/dist/watch.d.ts.map +1 -1
- package/package.json +10 -4
package/dist/index.esm.mjs
CHANGED
|
@@ -55,8 +55,9 @@ function cloneObject(data) {
|
|
|
55
55
|
if (data instanceof Date) {
|
|
56
56
|
return new Date(data);
|
|
57
57
|
}
|
|
58
|
+
const isBlobInstance = typeof Blob !== 'undefined' && data instanceof Blob;
|
|
58
59
|
const isFileListInstance = typeof FileList !== 'undefined' && data instanceof FileList;
|
|
59
|
-
if (isWeb && (
|
|
60
|
+
if (isWeb && (isBlobInstance || isFileListInstance)) {
|
|
60
61
|
return data;
|
|
61
62
|
}
|
|
62
63
|
const isArray = Array.isArray(data);
|
|
@@ -96,6 +97,16 @@ const INPUT_VALIDATION_RULES = {
|
|
|
96
97
|
required: 'required',
|
|
97
98
|
validate: 'validate',
|
|
98
99
|
};
|
|
100
|
+
const REGISTER_VALIDATION_RULES = [
|
|
101
|
+
INPUT_VALIDATION_RULES.required,
|
|
102
|
+
INPUT_VALIDATION_RULES.min,
|
|
103
|
+
INPUT_VALIDATION_RULES.max,
|
|
104
|
+
INPUT_VALIDATION_RULES.minLength,
|
|
105
|
+
INPUT_VALIDATION_RULES.maxLength,
|
|
106
|
+
INPUT_VALIDATION_RULES.pattern,
|
|
107
|
+
INPUT_VALIDATION_RULES.validate,
|
|
108
|
+
];
|
|
109
|
+
const FORM_ERROR_TYPE = 'form';
|
|
99
110
|
const ROOT_ERROR_TYPE = 'root';
|
|
100
111
|
const PROTOTYPE_KEYWORDS = ['__proto__', 'constructor', 'prototype'];
|
|
101
112
|
|
|
@@ -274,33 +285,14 @@ function useResyncOnReconnect(getInitialValue) {
|
|
|
274
285
|
}
|
|
275
286
|
|
|
276
287
|
/**
|
|
277
|
-
* Subscribes to
|
|
278
|
-
*
|
|
279
|
-
* @remarks
|
|
280
|
-
* [API](https://react-hook-form.com/docs/useformstate) • [Demo](https://codesandbox.io/s/useformstate-75xly)
|
|
288
|
+
* Subscribes to form state with re-renders isolated to this hook.
|
|
289
|
+
* Optionally scope to specific field names to minimize re-render surface.
|
|
281
290
|
*
|
|
282
|
-
* @
|
|
291
|
+
* @see [API](https://react-hook-form.com/docs/useformstate)
|
|
283
292
|
*
|
|
284
293
|
* @example
|
|
285
294
|
* ```tsx
|
|
286
|
-
*
|
|
287
|
-
* const { register, handleSubmit, control } = useForm({
|
|
288
|
-
* defaultValues: {
|
|
289
|
-
* firstName: "firstName"
|
|
290
|
-
* }});
|
|
291
|
-
* const { dirtyFields } = useFormState({
|
|
292
|
-
* control
|
|
293
|
-
* });
|
|
294
|
-
* const onSubmit = (data) => console.log(data);
|
|
295
|
-
*
|
|
296
|
-
* return (
|
|
297
|
-
* <form onSubmit={handleSubmit(onSubmit)}>
|
|
298
|
-
* <input {...register("firstName")} placeholder="First Name" />
|
|
299
|
-
* {dirtyFields.firstName && <p>Field is dirty.</p>}
|
|
300
|
-
* <input type="submit" />
|
|
301
|
-
* </form>
|
|
302
|
-
* );
|
|
303
|
-
* }
|
|
295
|
+
* const { errors, isDirty } = useFormState({ control, name: "email" });
|
|
304
296
|
* ```
|
|
305
297
|
*/
|
|
306
298
|
function useFormState(props) {
|
|
@@ -341,7 +333,7 @@ function useFormState(props) {
|
|
|
341
333
|
unsubscribe();
|
|
342
334
|
snapshot(!disabled, getCurrentFormState);
|
|
343
335
|
};
|
|
344
|
-
}, [name, disabled, exact, resyncIfNeeded, snapshot]);
|
|
336
|
+
}, [control, name, disabled, exact, resyncIfNeeded, snapshot]);
|
|
345
337
|
React.useEffect(() => {
|
|
346
338
|
_localProxyFormState.current.isValid && control._setValid(true);
|
|
347
339
|
}, [control]);
|
|
@@ -364,19 +356,15 @@ var generateWatchOutput = (names, _names, formValues, isGlobal, defaultValue) =>
|
|
|
364
356
|
};
|
|
365
357
|
|
|
366
358
|
/**
|
|
367
|
-
*
|
|
359
|
+
* Subscribes to field value changes and isolates re-renders to the hook level.
|
|
368
360
|
*
|
|
369
|
-
* @
|
|
370
|
-
*
|
|
371
|
-
* [API](https://react-hook-form.com/docs/usewatch) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-usewatch-h9i5e)
|
|
361
|
+
* @see [API](https://react-hook-form.com/docs/usewatch)
|
|
372
362
|
*
|
|
373
363
|
* @example
|
|
374
364
|
* ```tsx
|
|
375
|
-
* const { control
|
|
376
|
-
* const
|
|
377
|
-
*
|
|
378
|
-
* control,
|
|
379
|
-
* })
|
|
365
|
+
* const email = useWatch({ control, name: "email" });
|
|
366
|
+
* const all = useWatch({ control });
|
|
367
|
+
* const adult = useWatch({ control, name: "age", compute: (v) => v >= 18 });
|
|
380
368
|
* ```
|
|
381
369
|
*/
|
|
382
370
|
function useWatch(props) {
|
|
@@ -384,7 +372,6 @@ function useWatch(props) {
|
|
|
384
372
|
const { control = formControl, name, defaultValue, disabled, exact, compute, } = props || {};
|
|
385
373
|
const _defaultValue = React.useRef(defaultValue);
|
|
386
374
|
const _compute = React.useRef(compute);
|
|
387
|
-
const _computeFormValues = React.useRef(undefined);
|
|
388
375
|
const _prevControl = React.useRef(control);
|
|
389
376
|
const _prevName = React.useRef(name);
|
|
390
377
|
_compute.current = compute;
|
|
@@ -393,6 +380,7 @@ function useWatch(props) {
|
|
|
393
380
|
return _compute.current ? _compute.current(defaultValue) : defaultValue;
|
|
394
381
|
};
|
|
395
382
|
const [value, updateValue] = React.useState(getInitialOutput);
|
|
383
|
+
const _computeFormValues = React.useRef(value);
|
|
396
384
|
const getCurrentOutput = React.useCallback((values) => {
|
|
397
385
|
const formValues = generateWatchOutput(name, control._names, values || control._formValues, false, _defaultValue.current);
|
|
398
386
|
return _compute.current ? _compute.current(formValues) : formValues;
|
|
@@ -466,27 +454,15 @@ function useWatch(props) {
|
|
|
466
454
|
}
|
|
467
455
|
|
|
468
456
|
/**
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
* @remarks
|
|
472
|
-
* [API](https://react-hook-form.com/docs/usecontroller) • [Demo](https://codesandbox.io/s/usecontroller-0o8px)
|
|
473
|
-
*
|
|
474
|
-
* @param props - the path name to the form field value, and validation rules.
|
|
457
|
+
* Hook for controlled inputs. Returns `field`, `fieldState`, and `formState`.
|
|
458
|
+
* Re-renders are isolated to the hook level.
|
|
475
459
|
*
|
|
476
|
-
* @
|
|
460
|
+
* @see [API](https://react-hook-form.com/docs/usecontroller)
|
|
477
461
|
*
|
|
478
462
|
* @example
|
|
479
463
|
* ```tsx
|
|
480
|
-
*
|
|
481
|
-
*
|
|
482
|
-
* return (
|
|
483
|
-
* <div>
|
|
484
|
-
* <input {...field} placeholder={props.name} />
|
|
485
|
-
* <p>{fieldState.isTouched && "Touched"}</p>
|
|
486
|
-
* <p>{formState.isSubmitted ? "submitted" : ""}</p>
|
|
487
|
-
* </div>
|
|
488
|
-
* );
|
|
489
|
-
* }
|
|
464
|
+
* const { field, fieldState } = useController({ control, name: "email" });
|
|
465
|
+
* return <input {...field} />;
|
|
490
466
|
* ```
|
|
491
467
|
*/
|
|
492
468
|
function useController(props) {
|
|
@@ -575,6 +551,7 @@ function useController(props) {
|
|
|
575
551
|
const field = get(control._fields, name);
|
|
576
552
|
if (field && field._f && elm) {
|
|
577
553
|
field._f.ref = _proxyRef.current;
|
|
554
|
+
field._f._c = true;
|
|
578
555
|
}
|
|
579
556
|
}, [control._fields, name]);
|
|
580
557
|
const field = React.useMemo(() => ({
|
|
@@ -599,6 +576,7 @@ function useController(props) {
|
|
|
599
576
|
const field = get(control._fields, name);
|
|
600
577
|
if (field && field._f) {
|
|
601
578
|
field._f.mount = value;
|
|
579
|
+
field._f._c = value;
|
|
602
580
|
}
|
|
603
581
|
};
|
|
604
582
|
updateMounted(name, true);
|
|
@@ -611,7 +589,13 @@ function useController(props) {
|
|
|
611
589
|
set(control._formValues, name, value);
|
|
612
590
|
}
|
|
613
591
|
}
|
|
614
|
-
!isArrayField &&
|
|
592
|
+
!isArrayField &&
|
|
593
|
+
control.register(name, {
|
|
594
|
+
..._props.current.rules,
|
|
595
|
+
...(isBoolean(_props.current.disabled)
|
|
596
|
+
? { disabled: _props.current.disabled }
|
|
597
|
+
: {}),
|
|
598
|
+
});
|
|
615
599
|
if (_proxyRef.current) {
|
|
616
600
|
const field = get(control._fields, name);
|
|
617
601
|
if (field && field._f) {
|
|
@@ -640,48 +624,50 @@ function useController(props) {
|
|
|
640
624
|
}
|
|
641
625
|
|
|
642
626
|
/**
|
|
643
|
-
* Component
|
|
627
|
+
* Component wrapper around `useController` for controlled inputs.
|
|
644
628
|
*
|
|
645
|
-
* @
|
|
646
|
-
* [API](https://react-hook-form.com/docs/usecontroller/controller) • [Demo](https://codesandbox.io/s/react-hook-form-v6-controller-ts-jwyzw) • [Video](https://www.youtube.com/watch?v=N2UNk_UCVyA)
|
|
629
|
+
* @see [API](https://react-hook-form.com/docs/usecontroller/controller)
|
|
647
630
|
*
|
|
648
|
-
* @
|
|
631
|
+
* @example
|
|
632
|
+
* ```tsx
|
|
633
|
+
* <Controller
|
|
634
|
+
* control={control}
|
|
635
|
+
* name="test"
|
|
636
|
+
* render={({ field, fieldState, formState }) => <input {...field} />}
|
|
637
|
+
* />
|
|
638
|
+
* ```
|
|
639
|
+
*/
|
|
640
|
+
const Controller = (props) => props.render(useController(props));
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Displays the validation error for a single field.
|
|
644
|
+
* Reads from `control` when provided, otherwise from the nearest `FormProvider`.
|
|
649
645
|
*
|
|
650
|
-
* @
|
|
646
|
+
* @see [API](https://react-hook-form.com/docs/useformstate/errormessage)
|
|
651
647
|
*
|
|
652
648
|
* @example
|
|
653
649
|
* ```tsx
|
|
654
|
-
*
|
|
655
|
-
*
|
|
656
|
-
*
|
|
657
|
-
*
|
|
658
|
-
* }
|
|
659
|
-
* });
|
|
660
|
-
*
|
|
661
|
-
* return (
|
|
662
|
-
* <form>
|
|
663
|
-
* <Controller
|
|
664
|
-
* control={control}
|
|
665
|
-
* name="test"
|
|
666
|
-
* render={({ field: { onChange, onBlur, value, ref }, formState, fieldState }) => (
|
|
667
|
-
* <>
|
|
668
|
-
* <input
|
|
669
|
-
* onChange={onChange} // send value to hook form
|
|
670
|
-
* onBlur={onBlur} // notify when input is touched
|
|
671
|
-
* value={value} // return updated value
|
|
672
|
-
* ref={ref} // set ref for focus management
|
|
673
|
-
* />
|
|
674
|
-
* <p>{formState.isSubmitted ? "submitted" : ""}</p>
|
|
675
|
-
* <p>{fieldState.isTouched ? "touched" : ""}</p>
|
|
676
|
-
* </>
|
|
677
|
-
* )}
|
|
678
|
-
* />
|
|
679
|
-
* </form>
|
|
680
|
-
* );
|
|
681
|
-
* }
|
|
650
|
+
* <ErrorMessage control={control} name="email" as="p" />
|
|
651
|
+
* <ErrorMessage name="email" as="span" />
|
|
652
|
+
* <ErrorMessage control={control} name="email"
|
|
653
|
+
* render={({ message }) => <Alert>{message}</Alert>} />
|
|
682
654
|
* ```
|
|
683
655
|
*/
|
|
684
|
-
const
|
|
656
|
+
const ErrorMessage = ({ as, control, name, render, }) => {
|
|
657
|
+
const { errors } = useFormState({
|
|
658
|
+
control,
|
|
659
|
+
name: name,
|
|
660
|
+
});
|
|
661
|
+
const error = get(errors, name);
|
|
662
|
+
if (!error) {
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
665
|
+
const message = error.message || '';
|
|
666
|
+
if (render) {
|
|
667
|
+
return render({ message, messages: error.types });
|
|
668
|
+
}
|
|
669
|
+
return React.createElement(as || React.Fragment, null, message);
|
|
670
|
+
};
|
|
685
671
|
|
|
686
672
|
var generateId = () => typeof crypto !== 'undefined' && crypto.randomUUID
|
|
687
673
|
? crypto.randomUUID()
|
|
@@ -715,7 +701,7 @@ var isWatched = (name, _names, isBlurEvent) => {
|
|
|
715
701
|
return false;
|
|
716
702
|
};
|
|
717
703
|
|
|
718
|
-
const iterateFieldsByAction = (fields, action, fieldsNames
|
|
704
|
+
const iterateFieldsByAction = (fields, action, fieldsNames) => {
|
|
719
705
|
for (const key of fieldsNames || Object.keys(fields)) {
|
|
720
706
|
if (key === '_f') {
|
|
721
707
|
continue;
|
|
@@ -724,21 +710,21 @@ const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
|
|
|
724
710
|
if (field) {
|
|
725
711
|
const { _f } = field;
|
|
726
712
|
if (_f) {
|
|
727
|
-
if (_f.refs && _f.refs[0] && action(_f.refs[0],
|
|
713
|
+
if (_f.refs && _f.refs[0] && action(_f.refs[0], _f.name)) {
|
|
728
714
|
return true;
|
|
729
715
|
}
|
|
730
|
-
else if (_f.ref && action(_f.ref, _f.name)
|
|
716
|
+
else if (_f.ref && action(_f.ref, _f.name)) {
|
|
731
717
|
return true;
|
|
732
718
|
}
|
|
733
719
|
else {
|
|
734
720
|
if (iterateFieldsByAction(field, action)) {
|
|
735
|
-
|
|
721
|
+
return true;
|
|
736
722
|
}
|
|
737
723
|
}
|
|
738
724
|
}
|
|
739
725
|
else if (isObject(field) || Array.isArray(field)) {
|
|
740
726
|
if (iterateFieldsByAction(field, action)) {
|
|
741
|
-
|
|
727
|
+
return true;
|
|
742
728
|
}
|
|
743
729
|
}
|
|
744
730
|
}
|
|
@@ -839,7 +825,7 @@ var getValueAndMessage = (validationData) => isObject(validationData) && !isRege
|
|
|
839
825
|
};
|
|
840
826
|
|
|
841
827
|
var validateField = async (field, disabledFieldNames, formValues, validateAllFieldCriteria, shouldUseNativeValidation, isFieldArray) => {
|
|
842
|
-
const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, } = field._f;
|
|
828
|
+
const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, _c, } = field._f;
|
|
843
829
|
const inputValue = get(formValues, name);
|
|
844
830
|
if (!mount || disabledFieldNames.has(name)) {
|
|
845
831
|
return {};
|
|
@@ -849,7 +835,8 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
|
|
|
849
835
|
if (shouldUseNativeValidation && inputRef.reportValidity) {
|
|
850
836
|
const validityMessage = isBoolean(message) ? '' : message || '';
|
|
851
837
|
if (refs) {
|
|
852
|
-
refs.forEach((ref) => ref.setCustomValidity
|
|
838
|
+
refs.forEach((ref) => isFunction(ref.setCustomValidity) &&
|
|
839
|
+
ref.setCustomValidity(validityMessage));
|
|
853
840
|
}
|
|
854
841
|
else {
|
|
855
842
|
inputRef.setCustomValidity(validityMessage);
|
|
@@ -864,7 +851,7 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
|
|
|
864
851
|
const isEmpty = ((valueAsNumber || isFileInput(ref)) &&
|
|
865
852
|
isUndefined(ref.value) &&
|
|
866
853
|
isUndefined(inputValue)) ||
|
|
867
|
-
(isHTMLElement(ref) && ref.value === '') ||
|
|
854
|
+
(isHTMLElement(ref) && ref.value === '' && !_c) ||
|
|
868
855
|
inputValue === '' ||
|
|
869
856
|
(Array.isArray(inputValue) && !inputValue.length);
|
|
870
857
|
const appendErrorsCurry = appendErrors.bind(null, name, validateAllFieldCriteria, error);
|
|
@@ -1004,7 +991,9 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
|
|
|
1004
991
|
...validateError,
|
|
1005
992
|
...appendErrorsCurry(key, validateError.message),
|
|
1006
993
|
};
|
|
1007
|
-
|
|
994
|
+
if (!validateAllFieldCriteria) {
|
|
995
|
+
setCustomValidity(validateError.message);
|
|
996
|
+
}
|
|
1008
997
|
if (validateAllFieldCriteria) {
|
|
1009
998
|
error[name] = validationResult;
|
|
1010
999
|
}
|
|
@@ -1132,48 +1121,27 @@ var updateAt = (fieldValues, index, value) => {
|
|
|
1132
1121
|
};
|
|
1133
1122
|
|
|
1134
1123
|
/**
|
|
1135
|
-
*
|
|
1136
|
-
*
|
|
1137
|
-
* @remarks
|
|
1138
|
-
* [API](https://react-hook-form.com/docs/usefieldarray) • [Demo](https://codesandbox.io/s/react-hook-form-usefieldarray-ssugn)
|
|
1139
|
-
*
|
|
1140
|
-
* @param props - useFieldArray props
|
|
1124
|
+
* Hook for dynamic field arrays. Provides `fields` and mutation methods:
|
|
1125
|
+
* `append`, `prepend`, `remove`, `insert`, `swap`, `move`, `update`, `replace`.
|
|
1141
1126
|
*
|
|
1142
|
-
* @
|
|
1127
|
+
* @see [API](https://react-hook-form.com/docs/usefieldarray)
|
|
1143
1128
|
*
|
|
1144
1129
|
* @example
|
|
1145
1130
|
* ```tsx
|
|
1146
|
-
*
|
|
1147
|
-
*
|
|
1148
|
-
* defaultValues: {
|
|
1149
|
-
* test: []
|
|
1150
|
-
* }
|
|
1151
|
-
* });
|
|
1152
|
-
* const { fields, append } = useFieldArray({
|
|
1153
|
-
* control,
|
|
1154
|
-
* name: "test"
|
|
1155
|
-
* });
|
|
1156
|
-
*
|
|
1157
|
-
* return (
|
|
1158
|
-
* <form onSubmit={handleSubmit(data => console.log(data))}>
|
|
1159
|
-
* {fields.map((item, index) => (
|
|
1160
|
-
* <input key={item.id} {...register(`test.${index}.firstName`)} />
|
|
1161
|
-
* ))}
|
|
1162
|
-
* <button type="button" onClick={() => append({ firstName: "bill" })}>
|
|
1163
|
-
* append
|
|
1164
|
-
* </button>
|
|
1165
|
-
* <input type="submit" />
|
|
1166
|
-
* </form>
|
|
1167
|
-
* );
|
|
1168
|
-
* }
|
|
1131
|
+
* const { fields, append } = useFieldArray({ control, name: "items" });
|
|
1132
|
+
* return fields.map((f, i) => <input key={f.id} {...register(`items.${i}.name`)} />);
|
|
1169
1133
|
* ```
|
|
1170
1134
|
*/
|
|
1171
1135
|
function useFieldArray(props) {
|
|
1172
1136
|
const formControl = useFormControlContext();
|
|
1173
1137
|
const { control = formControl, name, keyName = 'id', disabled, shouldUnregister, rules, } = props;
|
|
1174
|
-
const
|
|
1138
|
+
const getCurrentFieldArray = () => control._getFieldArray(name);
|
|
1139
|
+
const [fields, setFields] = React.useState(getCurrentFieldArray);
|
|
1175
1140
|
const ids = React.useRef(control._getFieldArray(name).map(generateId));
|
|
1176
1141
|
const _actioned = React.useRef(false);
|
|
1142
|
+
const { resyncIfNeeded, snapshot } = useResyncOnReconnect(getCurrentFieldArray);
|
|
1143
|
+
const _prevControl = React.useRef(control);
|
|
1144
|
+
const _prevName = React.useRef(name);
|
|
1177
1145
|
if (!disabled) {
|
|
1178
1146
|
control._names.array.add(name);
|
|
1179
1147
|
}
|
|
@@ -1185,7 +1153,23 @@ function useFieldArray(props) {
|
|
|
1185
1153
|
if (disabled) {
|
|
1186
1154
|
return;
|
|
1187
1155
|
}
|
|
1188
|
-
|
|
1156
|
+
if (_prevControl.current === control && _prevName.current === name) {
|
|
1157
|
+
resyncIfNeeded(true, getCurrentFieldArray, (fieldValues) => {
|
|
1158
|
+
setFields(fieldValues);
|
|
1159
|
+
ids.current = fieldValues.map(generateId);
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
else {
|
|
1163
|
+
_prevControl.current = control;
|
|
1164
|
+
_prevName.current = name;
|
|
1165
|
+
const fieldValues = getCurrentFieldArray();
|
|
1166
|
+
if (!deepEqual(fields, fieldValues)) {
|
|
1167
|
+
setFields(fieldValues);
|
|
1168
|
+
ids.current = fieldValues.map(generateId);
|
|
1169
|
+
}
|
|
1170
|
+
snapshot(true, getCurrentFieldArray);
|
|
1171
|
+
}
|
|
1172
|
+
const unsubscribe = control._subjects.array.subscribe({
|
|
1189
1173
|
next: ({ values, name: fieldArrayName, }) => {
|
|
1190
1174
|
if (fieldArrayName === name || !fieldArrayName) {
|
|
1191
1175
|
const fieldValues = get(values, name);
|
|
@@ -1200,7 +1184,11 @@ function useFieldArray(props) {
|
|
|
1200
1184
|
}
|
|
1201
1185
|
},
|
|
1202
1186
|
}).unsubscribe;
|
|
1203
|
-
|
|
1187
|
+
return () => {
|
|
1188
|
+
unsubscribe();
|
|
1189
|
+
snapshot(true, getCurrentFieldArray);
|
|
1190
|
+
};
|
|
1191
|
+
}, [control, name, disabled, resyncIfNeeded, snapshot]);
|
|
1204
1192
|
const updateValues = React.useCallback((updatedFieldArrayValues) => {
|
|
1205
1193
|
_actioned.current = true;
|
|
1206
1194
|
control._setFieldArray(name, updatedFieldArrayValues);
|
|
@@ -1312,7 +1300,9 @@ function useFieldArray(props) {
|
|
|
1312
1300
|
ids.current = updatedFieldArrayValues.map(generateId);
|
|
1313
1301
|
updateValues([...updatedFieldArrayValues]);
|
|
1314
1302
|
setFields([...updatedFieldArrayValues]);
|
|
1315
|
-
control._setFieldArray(name, [...updatedFieldArrayValues], (data) => data
|
|
1303
|
+
control._setFieldArray(name, [...updatedFieldArrayValues], (data) => Array.isArray(data)
|
|
1304
|
+
? data.slice(0, updatedFieldArrayValues.length)
|
|
1305
|
+
: data, {});
|
|
1316
1306
|
};
|
|
1317
1307
|
React.useEffect(() => {
|
|
1318
1308
|
if (disabled) {
|
|
@@ -1363,10 +1353,23 @@ function useFieldArray(props) {
|
|
|
1363
1353
|
else {
|
|
1364
1354
|
const field = get(control._fields, name);
|
|
1365
1355
|
if (field && field._f) {
|
|
1366
|
-
validateField(field, control._names.disabled, control._formValues, control._options.criteriaMode === VALIDATION_MODE.all, control._options.shouldUseNativeValidation, true).then((error) =>
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1356
|
+
validateField(field, control._names.disabled, control._formValues, control._options.criteriaMode === VALIDATION_MODE.all, control._options.shouldUseNativeValidation, true).then((error) => {
|
|
1357
|
+
if (!isEmptyObject(error)) {
|
|
1358
|
+
control._subjects.state.next({
|
|
1359
|
+
errors: updateFieldArrayRootError(control._formState.errors, error, name),
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
else {
|
|
1363
|
+
const existingError = get(control._formState.errors, name);
|
|
1364
|
+
if (existingError && existingError[ROOT_ERROR_TYPE]) {
|
|
1365
|
+
unset(control._formState.errors, `${name}.${ROOT_ERROR_TYPE}`);
|
|
1366
|
+
control._subjects.state.next({
|
|
1367
|
+
errors: control._formState
|
|
1368
|
+
.errors,
|
|
1369
|
+
});
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
});
|
|
1370
1373
|
}
|
|
1371
1374
|
}
|
|
1372
1375
|
}
|
|
@@ -1394,7 +1397,8 @@ function useFieldArray(props) {
|
|
|
1394
1397
|
}, [fields, name, control, disabled]);
|
|
1395
1398
|
React.useEffect(() => {
|
|
1396
1399
|
if (!disabled) {
|
|
1397
|
-
|
|
1400
|
+
isUndefined(get(control._formValues, name)) &&
|
|
1401
|
+
control._setFieldArray(name);
|
|
1398
1402
|
}
|
|
1399
1403
|
return () => {
|
|
1400
1404
|
control._state.actionArrayLengths.delete(name);
|
|
@@ -1482,13 +1486,15 @@ const FieldArray = (props) => props.render(useFieldArray(props));
|
|
|
1482
1486
|
|
|
1483
1487
|
const isFileLike = (value) => (typeof Blob !== 'undefined' && value instanceof Blob) ||
|
|
1484
1488
|
(typeof File !== 'undefined' && value instanceof File);
|
|
1489
|
+
const isFileListLike = (value) => typeof FileList !== 'undefined' && value instanceof FileList;
|
|
1485
1490
|
const flatten = (obj) => {
|
|
1486
1491
|
const output = {};
|
|
1487
1492
|
for (const key of Object.keys(obj)) {
|
|
1488
1493
|
if (isObjectType(obj[key]) &&
|
|
1489
1494
|
obj[key] !== null &&
|
|
1490
1495
|
!isDateObject(obj[key]) &&
|
|
1491
|
-
!isFileLike(obj[key])
|
|
1496
|
+
!isFileLike(obj[key]) &&
|
|
1497
|
+
!isFileListLike(obj[key])) {
|
|
1492
1498
|
const nested = flatten(obj[key]);
|
|
1493
1499
|
for (const nestedKey of Object.keys(nested)) {
|
|
1494
1500
|
output[`${key}.${nestedKey}`] = nested[nestedKey];
|
|
@@ -1505,7 +1511,18 @@ function jsonToFormData(json) {
|
|
|
1505
1511
|
const result = new FormData();
|
|
1506
1512
|
const flattenFormValues = flatten(json);
|
|
1507
1513
|
for (const key in flattenFormValues) {
|
|
1508
|
-
|
|
1514
|
+
const value = flattenFormValues[key];
|
|
1515
|
+
if (isUndefined(value)) {
|
|
1516
|
+
continue;
|
|
1517
|
+
}
|
|
1518
|
+
if (typeof FileList !== 'undefined' && value instanceof FileList) {
|
|
1519
|
+
for (let index = 0; index < value.length; index++) {
|
|
1520
|
+
const file = value[index];
|
|
1521
|
+
file && result.append(key, file);
|
|
1522
|
+
}
|
|
1523
|
+
continue;
|
|
1524
|
+
}
|
|
1525
|
+
result.append(key, value);
|
|
1509
1526
|
}
|
|
1510
1527
|
return result;
|
|
1511
1528
|
}
|
|
@@ -1524,64 +1541,28 @@ function noop() { }
|
|
|
1524
1541
|
const HookFormContext = React.createContext(null);
|
|
1525
1542
|
HookFormContext.displayName = 'HookFormContext';
|
|
1526
1543
|
/**
|
|
1527
|
-
*
|
|
1528
|
-
*
|
|
1529
|
-
* @remarks
|
|
1530
|
-
* [API](https://react-hook-form.com/docs/useformcontext) • [Demo](https://codesandbox.io/s/react-hook-form-v7-form-context-ytudi)
|
|
1544
|
+
* Retrieves all `useForm` methods from the nearest `FormProvider`.
|
|
1545
|
+
* Use in deeply nested components to avoid prop-drilling.
|
|
1531
1546
|
*
|
|
1532
|
-
* @
|
|
1547
|
+
* @see [API](https://react-hook-form.com/docs/useformcontext)
|
|
1533
1548
|
*
|
|
1534
1549
|
* @example
|
|
1535
1550
|
* ```tsx
|
|
1536
|
-
*
|
|
1537
|
-
* const methods = useForm();
|
|
1538
|
-
* const onSubmit = data => console.log(data);
|
|
1539
|
-
*
|
|
1540
|
-
* return (
|
|
1541
|
-
* <FormProvider {...methods} >
|
|
1542
|
-
* <form onSubmit={methods.handleSubmit(onSubmit)}>
|
|
1543
|
-
* <NestedInput />
|
|
1544
|
-
* <input type="submit" />
|
|
1545
|
-
* </form>
|
|
1546
|
-
* </FormProvider>
|
|
1547
|
-
* );
|
|
1548
|
-
* }
|
|
1549
|
-
*
|
|
1550
|
-
* function NestedInput() {
|
|
1551
|
-
* const { register } = useFormContext(); // retrieve all hook methods
|
|
1552
|
-
* return <input {...register("test")} />;
|
|
1553
|
-
* }
|
|
1551
|
+
* const { register } = useFormContext<FormValues>();
|
|
1554
1552
|
* ```
|
|
1555
1553
|
*/
|
|
1556
1554
|
const useFormContext = () => React.useContext(HookFormContext);
|
|
1557
1555
|
/**
|
|
1558
|
-
*
|
|
1559
|
-
*
|
|
1560
|
-
* @remarks
|
|
1561
|
-
* [API](https://react-hook-form.com/docs/useformcontext) • [Demo](https://codesandbox.io/s/react-hook-form-v7-form-context-ytudi)
|
|
1556
|
+
* Provides all `useForm` methods to the component tree via React Context.
|
|
1557
|
+
* Pair with `useFormContext` to consume them in any descendant.
|
|
1562
1558
|
*
|
|
1563
|
-
* @
|
|
1559
|
+
* @see [API](https://react-hook-form.com/docs/useformcontext)
|
|
1564
1560
|
*
|
|
1565
1561
|
* @example
|
|
1566
1562
|
* ```tsx
|
|
1567
|
-
*
|
|
1568
|
-
*
|
|
1569
|
-
*
|
|
1570
|
-
*
|
|
1571
|
-
* return (
|
|
1572
|
-
* <FormProvider {...methods} >
|
|
1573
|
-
* <form onSubmit={methods.handleSubmit(onSubmit)}>
|
|
1574
|
-
* <NestedInput />
|
|
1575
|
-
* <input type="submit" />
|
|
1576
|
-
* </form>
|
|
1577
|
-
* </FormProvider>
|
|
1578
|
-
* );
|
|
1579
|
-
* }
|
|
1580
|
-
*
|
|
1581
|
-
* function NestedInput() {
|
|
1582
|
-
* const { register } = useFormContext(); // retrieve all hook methods
|
|
1583
|
-
* return <input {...register("test")} />;
|
|
1584
|
-
* }
|
|
1563
|
+
* <FormProvider {...methods}>
|
|
1564
|
+
* <form onSubmit={methods.handleSubmit(onSubmit)}>{children}</form>
|
|
1565
|
+
* </FormProvider>
|
|
1585
1566
|
* ```
|
|
1586
1567
|
*/
|
|
1587
1568
|
const FormProvider = ({ children, watch, getValues, getErrors, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, resetDefaultValues, handleSubmit, unregister, control, register, setFocus, subscribe, }) => {
|
|
@@ -1635,25 +1616,17 @@ function defaultValidateStatus(status) {
|
|
|
1635
1616
|
return status >= 200 && status < 300;
|
|
1636
1617
|
}
|
|
1637
1618
|
/**
|
|
1638
|
-
* Form component
|
|
1639
|
-
*
|
|
1640
|
-
* @param props - to setup submission detail. {@link FormProps}
|
|
1619
|
+
* Form component that handles submission, including optional `action` fetch and server error wiring.
|
|
1641
1620
|
*
|
|
1642
|
-
* @
|
|
1621
|
+
* @see [API](https://react-hook-form.com/docs/useform/form)
|
|
1643
1622
|
*
|
|
1644
1623
|
* @example
|
|
1645
1624
|
* ```tsx
|
|
1646
|
-
*
|
|
1647
|
-
*
|
|
1648
|
-
*
|
|
1649
|
-
*
|
|
1650
|
-
*
|
|
1651
|
-
* <input {...register("name")} />
|
|
1652
|
-
* <p>{errors?.root?.server && 'Server error'}</p>
|
|
1653
|
-
* <button>Submit</button>
|
|
1654
|
-
* </Form>
|
|
1655
|
-
* );
|
|
1656
|
-
* }
|
|
1625
|
+
* <Form action="/api" control={control}>
|
|
1626
|
+
* <input {...register("name")} />
|
|
1627
|
+
* <p>{errors?.root?.server && 'Server error'}</p>
|
|
1628
|
+
* <button>Submit</button>
|
|
1629
|
+
* </Form>
|
|
1657
1630
|
* ```
|
|
1658
1631
|
*/
|
|
1659
1632
|
function Form(props) {
|
|
@@ -1778,16 +1751,15 @@ var createSubject = () => {
|
|
|
1778
1751
|
};
|
|
1779
1752
|
|
|
1780
1753
|
function extractFormValues(fieldsState, formValues) {
|
|
1781
|
-
const values = {};
|
|
1754
|
+
const values = (Array.isArray(fieldsState) ? [] : {});
|
|
1782
1755
|
for (const key in fieldsState) {
|
|
1783
1756
|
if (fieldsState.hasOwnProperty(key)) {
|
|
1784
1757
|
const fieldState = fieldsState[key];
|
|
1785
1758
|
const fieldValue = formValues[key];
|
|
1786
|
-
if (fieldState &&
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
}
|
|
1759
|
+
if (fieldState &&
|
|
1760
|
+
(isObject(fieldState) || Array.isArray(fieldState)) &&
|
|
1761
|
+
fieldValue) {
|
|
1762
|
+
values[key] = extractFormValues(fieldState, fieldValue);
|
|
1791
1763
|
}
|
|
1792
1764
|
else if (fieldsState[key]) {
|
|
1793
1765
|
values[key] = fieldValue;
|
|
@@ -2011,7 +1983,7 @@ var hasValidation = (options) => options.mount &&
|
|
|
2011
1983
|
|
|
2012
1984
|
function schemaErrorLookup(errors, _fields, name) {
|
|
2013
1985
|
const error = get(errors, name);
|
|
2014
|
-
if (error ||
|
|
1986
|
+
if ((error === null || error === void 0 ? void 0 : error.type) || (error === null || error === void 0 ? void 0 : error.message) || Array.isArray(error)) {
|
|
2015
1987
|
return {
|
|
2016
1988
|
error,
|
|
2017
1989
|
name,
|
|
@@ -2090,7 +2062,6 @@ const defaultOptions = {
|
|
|
2090
2062
|
reValidateMode: VALIDATION_MODE.onChange,
|
|
2091
2063
|
shouldFocusError: true,
|
|
2092
2064
|
};
|
|
2093
|
-
const FORM_ERROR_TYPE = 'form';
|
|
2094
2065
|
const updateDirtyFields = (dirtyFields, nextDirtyFields) => {
|
|
2095
2066
|
for (const key in dirtyFields) {
|
|
2096
2067
|
if (!(key in nextDirtyFields)) {
|
|
@@ -2165,24 +2136,35 @@ function createFormControl(props = {}) {
|
|
|
2165
2136
|
let _proxySubscribeFormState = {
|
|
2166
2137
|
..._proxyFormState,
|
|
2167
2138
|
};
|
|
2139
|
+
const _isTracked = (...keys) => keys.some((key) => _proxyFormState[key] || _proxySubscribeFormState[key]);
|
|
2168
2140
|
const _subjects = {
|
|
2169
2141
|
array: createSubject(),
|
|
2170
2142
|
state: createSubject(),
|
|
2171
2143
|
};
|
|
2172
2144
|
let _setValidCallId = 0;
|
|
2173
|
-
|
|
2145
|
+
let _resetCallId = 0;
|
|
2146
|
+
let shouldDisplayAllAssociatedErrors = _options.criteriaMode === VALIDATION_MODE.all;
|
|
2174
2147
|
const debounce = (name, callback) => (wait) => {
|
|
2175
2148
|
clearTimeout(timers[name]);
|
|
2176
2149
|
timers[name] = setTimeout(callback, wait);
|
|
2177
2150
|
};
|
|
2151
|
+
const cancelDelayedError = (name) => {
|
|
2152
|
+
clearTimeout(timers[name]);
|
|
2153
|
+
delete timers[name];
|
|
2154
|
+
delete delayErrorCallbacks[name];
|
|
2155
|
+
};
|
|
2156
|
+
const cancelDelayedErrorTree = (name) => {
|
|
2157
|
+
cancelDelayedError(name);
|
|
2158
|
+
const prefix = `${name}.`;
|
|
2159
|
+
for (const key of Object.keys(delayErrorCallbacks)) {
|
|
2160
|
+
key.startsWith(prefix) && cancelDelayedError(key);
|
|
2161
|
+
}
|
|
2162
|
+
};
|
|
2178
2163
|
const _setValid = async (shouldUpdateValid) => {
|
|
2179
2164
|
if (_state.keepIsValid) {
|
|
2180
2165
|
return;
|
|
2181
2166
|
}
|
|
2182
|
-
if (!_options.disabled &&
|
|
2183
|
-
(_proxyFormState.isValid ||
|
|
2184
|
-
_proxySubscribeFormState.isValid ||
|
|
2185
|
-
shouldUpdateValid)) {
|
|
2167
|
+
if (!_options.disabled && (_isTracked('isValid') || shouldUpdateValid)) {
|
|
2186
2168
|
const callId = ++_setValidCallId;
|
|
2187
2169
|
let isValid;
|
|
2188
2170
|
if (_options.resolver) {
|
|
@@ -2204,11 +2186,7 @@ function createFormControl(props = {}) {
|
|
|
2204
2186
|
}
|
|
2205
2187
|
};
|
|
2206
2188
|
const _updateIsValidating = (names, isValidating) => {
|
|
2207
|
-
if (!_options.disabled &&
|
|
2208
|
-
(_proxyFormState.isValidating ||
|
|
2209
|
-
_proxyFormState.validatingFields ||
|
|
2210
|
-
_proxySubscribeFormState.isValidating ||
|
|
2211
|
-
_proxySubscribeFormState.validatingFields)) {
|
|
2189
|
+
if (!_options.disabled && _isTracked('isValidating', 'validatingFields')) {
|
|
2212
2190
|
(names || _names.mount).forEach((name) => {
|
|
2213
2191
|
if (name) {
|
|
2214
2192
|
isValidating
|
|
@@ -2247,20 +2225,26 @@ function createFormControl(props = {}) {
|
|
|
2247
2225
|
unsetEmptyArray(_formState.errors, name);
|
|
2248
2226
|
}
|
|
2249
2227
|
const touchedFieldsArray = get(_formState.touchedFields, name);
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
shouldUpdateFieldsAndState &&
|
|
2253
|
-
Array.isArray(touchedFieldsArray)) {
|
|
2228
|
+
const shouldUpdateTouchedFields = shouldUpdateFieldsAndState && Array.isArray(touchedFieldsArray);
|
|
2229
|
+
if (shouldUpdateTouchedFields) {
|
|
2254
2230
|
const touchedFields = method(touchedFieldsArray, args.argA, args.argB);
|
|
2255
2231
|
shouldSetValues && set(_formState.touchedFields, name, touchedFields);
|
|
2256
2232
|
}
|
|
2257
|
-
|
|
2233
|
+
const dirtyFieldsArray = get(_formState.dirtyFields, name);
|
|
2234
|
+
if (shouldUpdateFieldsAndState && Array.isArray(dirtyFieldsArray)) {
|
|
2235
|
+
const dirtyFields = method(dirtyFieldsArray, args.argA, args.argB) || dirtyFieldsArray;
|
|
2236
|
+
shouldSetValues && set(_formState.dirtyFields, name, dirtyFields);
|
|
2237
|
+
}
|
|
2238
|
+
if (_isTracked('dirtyFields')) {
|
|
2258
2239
|
_updateDirtyFields();
|
|
2259
2240
|
}
|
|
2260
2241
|
_subjects.state.next({
|
|
2261
2242
|
name,
|
|
2262
2243
|
isDirty: _getDirty(name, values),
|
|
2263
2244
|
dirtyFields: _formState.dirtyFields,
|
|
2245
|
+
...(shouldUpdateTouchedFields && {
|
|
2246
|
+
touchedFields: _formState.touchedFields,
|
|
2247
|
+
}),
|
|
2264
2248
|
errors: _formState.errors,
|
|
2265
2249
|
isValid: _formState.isValid,
|
|
2266
2250
|
});
|
|
@@ -2277,11 +2261,14 @@ function createFormControl(props = {}) {
|
|
|
2277
2261
|
});
|
|
2278
2262
|
};
|
|
2279
2263
|
const _setErrors = (errors) => {
|
|
2264
|
+
Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
|
|
2265
|
+
const hasErrors = !isEmptyObject(errors);
|
|
2280
2266
|
_formState.errors = errors;
|
|
2281
2267
|
_subjects.state.next({
|
|
2282
2268
|
errors: _formState.errors,
|
|
2283
|
-
isValid: false,
|
|
2269
|
+
...(hasErrors ? { isValid: false } : {}),
|
|
2284
2270
|
});
|
|
2271
|
+
!hasErrors && _state.mount && _setValid();
|
|
2285
2272
|
};
|
|
2286
2273
|
const hasExplicitNullIntermediate = (name) => {
|
|
2287
2274
|
const segments = isKey(name) ? [name] : stringToPath(name);
|
|
@@ -2299,7 +2286,7 @@ function createFormControl(props = {}) {
|
|
|
2299
2286
|
}
|
|
2300
2287
|
return false;
|
|
2301
2288
|
};
|
|
2302
|
-
const
|
|
2289
|
+
const isStaleArrayField = (name) => {
|
|
2303
2290
|
if (!_state.actionArrayLengths.size) {
|
|
2304
2291
|
return false;
|
|
2305
2292
|
}
|
|
@@ -2326,13 +2313,19 @@ function createFormControl(props = {}) {
|
|
|
2326
2313
|
ownerPreActionLength = _state.actionArrayLengths.get(path);
|
|
2327
2314
|
}
|
|
2328
2315
|
node = node[key];
|
|
2316
|
+
if (isUndefined(node) &&
|
|
2317
|
+
ownerDepth !== -1 &&
|
|
2318
|
+
i > ownerDepth &&
|
|
2319
|
+
+segments[ownerDepth] < ownerPreActionLength) {
|
|
2320
|
+
return true;
|
|
2321
|
+
}
|
|
2329
2322
|
}
|
|
2330
2323
|
return false;
|
|
2331
2324
|
};
|
|
2332
2325
|
const updateValidAndValue = (name, shouldSkipSetValueAs, value, ref) => {
|
|
2333
2326
|
const field = get(_fields, name);
|
|
2334
2327
|
if (field) {
|
|
2335
|
-
if (hasExplicitNullIntermediate(name) ||
|
|
2328
|
+
if (hasExplicitNullIntermediate(name) || isStaleArrayField(name)) {
|
|
2336
2329
|
return;
|
|
2337
2330
|
}
|
|
2338
2331
|
const wasUnsetInFormValues = isUndefined(get(_formValues, name));
|
|
@@ -2343,10 +2336,16 @@ function createFormControl(props = {}) {
|
|
|
2343
2336
|
? set(_formValues, name, shouldSkipSetValueAs ? defaultValue : getFieldValue(field._f))
|
|
2344
2337
|
: setFieldValue(name, defaultValue);
|
|
2345
2338
|
if (_state.mount && !_state.action) {
|
|
2346
|
-
|
|
2339
|
+
if (_options.resolver &&
|
|
2340
|
+
_isTracked('isValidating', 'validatingFields')) {
|
|
2341
|
+
Promise.resolve().then(() => _setValid());
|
|
2342
|
+
}
|
|
2343
|
+
else {
|
|
2344
|
+
_setValid();
|
|
2345
|
+
}
|
|
2347
2346
|
if (wasUnsetInFormValues &&
|
|
2348
2347
|
_formState.isDirty &&
|
|
2349
|
-
(
|
|
2348
|
+
_isTracked('isDirty')) {
|
|
2350
2349
|
const isDirty = _getDirty();
|
|
2351
2350
|
if (!isDirty) {
|
|
2352
2351
|
_formState.isDirty = false;
|
|
@@ -2373,7 +2372,7 @@ function createFormControl(props = {}) {
|
|
|
2373
2372
|
if (!_options.disabled || shouldDirty === true) {
|
|
2374
2373
|
if (!isBlurEvent || shouldDirty) {
|
|
2375
2374
|
const isCurrentFieldPristine = deepEqual(get(_defaultValues, name), fieldValue);
|
|
2376
|
-
if (
|
|
2375
|
+
if (_isTracked('isDirty')) {
|
|
2377
2376
|
isPreviousDirty = _formState.isDirty;
|
|
2378
2377
|
_formState.isDirty = output.isDirty =
|
|
2379
2378
|
!isCurrentFieldPristine || _getDirty();
|
|
@@ -2391,8 +2390,7 @@ function createFormControl(props = {}) {
|
|
|
2391
2390
|
output.dirtyFields = _formState.dirtyFields;
|
|
2392
2391
|
shouldUpdateField =
|
|
2393
2392
|
shouldUpdateField ||
|
|
2394
|
-
((
|
|
2395
|
-
_proxySubscribeFormState.dirtyFields) &&
|
|
2393
|
+
(_isTracked('dirtyFields') &&
|
|
2396
2394
|
isPreviousDirty !== !isCurrentFieldPristine);
|
|
2397
2395
|
}
|
|
2398
2396
|
if (isBlurEvent) {
|
|
@@ -2402,8 +2400,7 @@ function createFormControl(props = {}) {
|
|
|
2402
2400
|
output.touchedFields = _formState.touchedFields;
|
|
2403
2401
|
shouldUpdateField =
|
|
2404
2402
|
shouldUpdateField ||
|
|
2405
|
-
((
|
|
2406
|
-
_proxySubscribeFormState.touchedFields) &&
|
|
2403
|
+
(_isTracked('touchedFields') &&
|
|
2407
2404
|
isPreviousFieldTouched !== isBlurEvent);
|
|
2408
2405
|
}
|
|
2409
2406
|
}
|
|
@@ -2413,7 +2410,7 @@ function createFormControl(props = {}) {
|
|
|
2413
2410
|
};
|
|
2414
2411
|
const shouldRenderByError = (name, isValid, error, fieldState) => {
|
|
2415
2412
|
const previousFieldError = get(_formState.errors, name);
|
|
2416
|
-
const shouldUpdateValid = (
|
|
2413
|
+
const shouldUpdateValid = _isTracked('isValid') &&
|
|
2417
2414
|
isBoolean(isValid) &&
|
|
2418
2415
|
_formState.isValid !== isValid;
|
|
2419
2416
|
if (_options.delayError && error) {
|
|
@@ -2421,8 +2418,7 @@ function createFormControl(props = {}) {
|
|
|
2421
2418
|
delayErrorCallbacks[name](_options.delayError);
|
|
2422
2419
|
}
|
|
2423
2420
|
else {
|
|
2424
|
-
|
|
2425
|
-
delete delayErrorCallbacks[name];
|
|
2421
|
+
cancelDelayedError(name);
|
|
2426
2422
|
error
|
|
2427
2423
|
? set(_formState.errors, name, error)
|
|
2428
2424
|
: unset(_formState.errors, name);
|
|
@@ -2445,55 +2441,76 @@ function createFormControl(props = {}) {
|
|
|
2445
2441
|
return await _options.resolver(_formValues, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation));
|
|
2446
2442
|
};
|
|
2447
2443
|
const executeSchemaAndUpdateState = async (names) => {
|
|
2444
|
+
const resetCallId = _resetCallId;
|
|
2448
2445
|
const { errors } = await _runSchema(names);
|
|
2446
|
+
if (resetCallId !== _resetCallId) {
|
|
2447
|
+
return errors;
|
|
2448
|
+
}
|
|
2449
2449
|
_updateIsValidating(names);
|
|
2450
2450
|
if (names) {
|
|
2451
2451
|
for (const name of names) {
|
|
2452
2452
|
const error = get(errors, name);
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2453
|
+
cancelDelayedErrorTree(name);
|
|
2454
|
+
const isFieldArrayRootError = _names.array.has(name) &&
|
|
2455
|
+
isObject(error) &&
|
|
2456
|
+
!Object.keys(error).some((key) => !Number.isNaN(Number(key)));
|
|
2457
|
+
const field = get(_fields, name);
|
|
2458
|
+
const hasNestedFields = isObject(field) && Object.keys(field).some((key) => key !== '_f');
|
|
2459
|
+
isFieldArrayRootError
|
|
2460
|
+
? updateFieldArrayRootError(_formState.errors, { [name]: error }, name)
|
|
2461
|
+
: (error === null || error === void 0 ? void 0 : error.type) ||
|
|
2462
|
+
(error === null || error === void 0 ? void 0 : error.message) ||
|
|
2463
|
+
Array.isArray(error) ||
|
|
2464
|
+
(isObject(error) && hasNestedFields)
|
|
2465
|
+
? set(_formState.errors, name, error)
|
|
2466
|
+
: unset(_formState.errors, name);
|
|
2460
2467
|
}
|
|
2461
2468
|
_formState.errors = { ..._formState.errors };
|
|
2462
2469
|
}
|
|
2463
2470
|
else {
|
|
2471
|
+
Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
|
|
2464
2472
|
_formState.errors = errors;
|
|
2465
2473
|
}
|
|
2466
2474
|
return errors;
|
|
2467
2475
|
};
|
|
2468
2476
|
const validateForm = async ({ name, eventType, }) => {
|
|
2469
|
-
if (
|
|
2470
|
-
const
|
|
2477
|
+
if (_options.validate) {
|
|
2478
|
+
const resetCallId = _resetCallId;
|
|
2479
|
+
const result = await _options.validate({
|
|
2471
2480
|
formValues: _formValues,
|
|
2472
2481
|
formState: _formState,
|
|
2473
2482
|
name,
|
|
2474
2483
|
eventType,
|
|
2475
2484
|
});
|
|
2485
|
+
if (resetCallId !== _resetCallId) {
|
|
2486
|
+
return true;
|
|
2487
|
+
}
|
|
2476
2488
|
if (isObject(result)) {
|
|
2489
|
+
let isValid = true;
|
|
2490
|
+
clearErrors(FORM_ERROR_TYPE);
|
|
2477
2491
|
for (const key in result) {
|
|
2478
2492
|
const error = result[key];
|
|
2479
2493
|
if (error) {
|
|
2494
|
+
isValid = false;
|
|
2480
2495
|
setError(`${FORM_ERROR_TYPE}.${key}`, {
|
|
2481
2496
|
message: isString(error.message) ? error.message : '',
|
|
2482
2497
|
type: error.type || INPUT_VALIDATION_RULES.validate,
|
|
2483
2498
|
});
|
|
2484
2499
|
}
|
|
2485
2500
|
}
|
|
2501
|
+
return isValid;
|
|
2486
2502
|
}
|
|
2487
2503
|
else if (isString(result) || !result) {
|
|
2488
2504
|
setError(FORM_ERROR_TYPE, {
|
|
2489
2505
|
message: result || '',
|
|
2490
2506
|
type: INPUT_VALIDATION_RULES.validate,
|
|
2491
2507
|
});
|
|
2508
|
+
return false;
|
|
2492
2509
|
}
|
|
2493
2510
|
else {
|
|
2494
2511
|
clearErrors(FORM_ERROR_TYPE);
|
|
2512
|
+
return true;
|
|
2495
2513
|
}
|
|
2496
|
-
return result;
|
|
2497
2514
|
}
|
|
2498
2515
|
return true;
|
|
2499
2516
|
};
|
|
@@ -2501,7 +2518,8 @@ function createFormControl(props = {}) {
|
|
|
2501
2518
|
valid: true,
|
|
2502
2519
|
runRootValidation: false,
|
|
2503
2520
|
}, }) => {
|
|
2504
|
-
|
|
2521
|
+
const resetCallId = _resetCallId;
|
|
2522
|
+
if (_options.validate && !context.runRootValidation) {
|
|
2505
2523
|
context.runRootValidation = true;
|
|
2506
2524
|
const result = await validateForm({
|
|
2507
2525
|
name,
|
|
@@ -2521,14 +2539,14 @@ function createFormControl(props = {}) {
|
|
|
2521
2539
|
if (_f) {
|
|
2522
2540
|
const isFieldArrayRoot = _names.array.has(_f.name);
|
|
2523
2541
|
const isPromiseFunction = field._f && hasPromiseValidation(field._f);
|
|
2524
|
-
const shouldTrackIsValidatingState =
|
|
2525
|
-
_proxyFormState.isValidating ||
|
|
2526
|
-
_proxySubscribeFormState.validatingFields ||
|
|
2527
|
-
_proxySubscribeFormState.isValidating;
|
|
2542
|
+
const shouldTrackIsValidatingState = _isTracked('isValidating', 'validatingFields');
|
|
2528
2543
|
if (isPromiseFunction && shouldTrackIsValidatingState) {
|
|
2529
2544
|
_updateIsValidating([_f.name], true);
|
|
2530
2545
|
}
|
|
2531
2546
|
const fieldError = await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !onlyCheckValid, isFieldArrayRoot);
|
|
2547
|
+
if (resetCallId !== _resetCallId) {
|
|
2548
|
+
return context.valid;
|
|
2549
|
+
}
|
|
2532
2550
|
if (isPromiseFunction && shouldTrackIsValidatingState) {
|
|
2533
2551
|
_updateIsValidating([_f.name]);
|
|
2534
2552
|
}
|
|
@@ -2538,12 +2556,14 @@ function createFormControl(props = {}) {
|
|
|
2538
2556
|
break;
|
|
2539
2557
|
}
|
|
2540
2558
|
}
|
|
2541
|
-
!onlyCheckValid
|
|
2542
|
-
(
|
|
2559
|
+
if (!onlyCheckValid) {
|
|
2560
|
+
cancelDelayedError(_f.name);
|
|
2561
|
+
get(fieldError, _f.name)
|
|
2543
2562
|
? isFieldArrayRoot
|
|
2544
2563
|
? updateFieldArrayRootError(_formState.errors, fieldError, _f.name)
|
|
2545
2564
|
: set(_formState.errors, _f.name, fieldError[_f.name])
|
|
2546
|
-
: unset(_formState.errors, _f.name)
|
|
2565
|
+
: unset(_formState.errors, _f.name);
|
|
2566
|
+
}
|
|
2547
2567
|
if (props.shouldUseNativeValidation && fieldError[_f.name]) {
|
|
2548
2568
|
break;
|
|
2549
2569
|
}
|
|
@@ -2629,11 +2649,25 @@ function createFormControl(props = {}) {
|
|
|
2629
2649
|
}
|
|
2630
2650
|
}
|
|
2631
2651
|
(options.shouldDirty || options.shouldTouch) &&
|
|
2632
|
-
updateTouchAndDirty(name,
|
|
2652
|
+
updateTouchAndDirty(name, field &&
|
|
2653
|
+
field._f &&
|
|
2654
|
+
!field._f.disabled &&
|
|
2655
|
+
(field._f.valueAsNumber ||
|
|
2656
|
+
field._f.valueAsDate ||
|
|
2657
|
+
field._f.setValueAs)
|
|
2658
|
+
? getFieldValueAs(value, field._f)
|
|
2659
|
+
: fieldValue, options.shouldTouch, options.shouldDirty, !skipRender);
|
|
2633
2660
|
options.shouldValidate &&
|
|
2634
2661
|
trigger(name, {
|
|
2635
2662
|
delayError: options.delayError,
|
|
2636
2663
|
});
|
|
2664
|
+
if (options.shouldValidate &&
|
|
2665
|
+
field &&
|
|
2666
|
+
field._f &&
|
|
2667
|
+
field._f.deps &&
|
|
2668
|
+
(!Array.isArray(field._f.deps) || field._f.deps.length > 0)) {
|
|
2669
|
+
trigger(field._f.deps);
|
|
2670
|
+
}
|
|
2637
2671
|
};
|
|
2638
2672
|
const setFieldValues = (name, value, options, skipClone = false, skipRender = false, skipValueRender = false) => {
|
|
2639
2673
|
if (_names.array.has(name)) {
|
|
@@ -2671,11 +2705,7 @@ function createFormControl(props = {}) {
|
|
|
2671
2705
|
name,
|
|
2672
2706
|
values: skipClone ? _formValues : cloneObject(_formValues),
|
|
2673
2707
|
});
|
|
2674
|
-
if ((
|
|
2675
|
-
_proxyFormState.dirtyFields ||
|
|
2676
|
-
_proxySubscribeFormState.isDirty ||
|
|
2677
|
-
_proxySubscribeFormState.dirtyFields) &&
|
|
2678
|
-
options.shouldDirty) {
|
|
2708
|
+
if (_isTracked('isDirty', 'dirtyFields') && options.shouldDirty) {
|
|
2679
2709
|
_updateDirtyFields();
|
|
2680
2710
|
if (!skipStateEmit) {
|
|
2681
2711
|
_subjects.state.next({
|
|
@@ -2685,6 +2715,10 @@ function createFormControl(props = {}) {
|
|
|
2685
2715
|
});
|
|
2686
2716
|
}
|
|
2687
2717
|
}
|
|
2718
|
+
options.shouldValidate &&
|
|
2719
|
+
trigger(name, {
|
|
2720
|
+
delayError: options.delayError,
|
|
2721
|
+
});
|
|
2688
2722
|
}
|
|
2689
2723
|
else {
|
|
2690
2724
|
const isEmpty = (Array.isArray(cloneValue) && !cloneValue.length) ||
|
|
@@ -2758,7 +2792,7 @@ function createFormControl(props = {}) {
|
|
|
2758
2792
|
: getEventValue(event);
|
|
2759
2793
|
const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
|
|
2760
2794
|
const hasNoValidationEffect = !hasValidation(field._f) &&
|
|
2761
|
-
!
|
|
2795
|
+
!_options.validate &&
|
|
2762
2796
|
!_options.resolver &&
|
|
2763
2797
|
!get(_formState.errors, name) &&
|
|
2764
2798
|
!field._f.deps;
|
|
@@ -2788,7 +2822,7 @@ function createFormControl(props = {}) {
|
|
|
2788
2822
|
});
|
|
2789
2823
|
if (shouldSkipValidation) {
|
|
2790
2824
|
if ((!hasNoValidationEffect || !_formState.isValid) &&
|
|
2791
|
-
(
|
|
2825
|
+
_isTracked('isValid')) {
|
|
2792
2826
|
if (_options.mode === 'onBlur') {
|
|
2793
2827
|
if (isBlurEvent) {
|
|
2794
2828
|
_setValid();
|
|
@@ -2801,7 +2835,7 @@ function createFormControl(props = {}) {
|
|
|
2801
2835
|
return (shouldRender &&
|
|
2802
2836
|
_subjects.state.next({ name, ...(watched ? {} : fieldState) }));
|
|
2803
2837
|
}
|
|
2804
|
-
if (!_options.resolver &&
|
|
2838
|
+
if (!_options.resolver && _options.validate) {
|
|
2805
2839
|
await validateForm({
|
|
2806
2840
|
name: name,
|
|
2807
2841
|
eventType: event.type,
|
|
@@ -2809,7 +2843,11 @@ function createFormControl(props = {}) {
|
|
|
2809
2843
|
}
|
|
2810
2844
|
!isBlurEvent && watched && _subjects.state.next({ ..._formState });
|
|
2811
2845
|
if (_options.resolver) {
|
|
2846
|
+
const resetCallId = _resetCallId;
|
|
2812
2847
|
const { errors } = await _runSchema([name]);
|
|
2848
|
+
if (resetCallId !== _resetCallId) {
|
|
2849
|
+
return;
|
|
2850
|
+
}
|
|
2813
2851
|
_updateIsValidating([name]);
|
|
2814
2852
|
_updateIsFieldValueUpdated(fieldValue);
|
|
2815
2853
|
if (!isFieldValueUpdated) {
|
|
@@ -2823,22 +2861,28 @@ function createFormControl(props = {}) {
|
|
|
2823
2861
|
isValid = isEmptyObject(errors);
|
|
2824
2862
|
}
|
|
2825
2863
|
else {
|
|
2864
|
+
const resetCallId = _resetCallId;
|
|
2826
2865
|
_updateIsValidating([name], true);
|
|
2827
2866
|
error = (await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation))[name];
|
|
2867
|
+
if (resetCallId !== _resetCallId) {
|
|
2868
|
+
return;
|
|
2869
|
+
}
|
|
2828
2870
|
_updateIsValidating([name]);
|
|
2829
2871
|
_updateIsFieldValueUpdated(fieldValue);
|
|
2830
2872
|
if (isFieldValueUpdated) {
|
|
2831
2873
|
if (error) {
|
|
2832
2874
|
isValid = false;
|
|
2833
2875
|
}
|
|
2834
|
-
else if (
|
|
2835
|
-
_proxySubscribeFormState.isValid) {
|
|
2876
|
+
else if (_isTracked('isValid')) {
|
|
2836
2877
|
isValid = await executeBuiltInValidation({
|
|
2837
2878
|
fields: _fields,
|
|
2838
2879
|
onlyCheckValid: true,
|
|
2839
2880
|
name: name,
|
|
2840
2881
|
eventType: event.type,
|
|
2841
2882
|
});
|
|
2883
|
+
if (resetCallId !== _resetCallId) {
|
|
2884
|
+
return;
|
|
2885
|
+
}
|
|
2842
2886
|
}
|
|
2843
2887
|
}
|
|
2844
2888
|
}
|
|
@@ -2862,11 +2906,15 @@ function createFormControl(props = {}) {
|
|
|
2862
2906
|
let validationResult;
|
|
2863
2907
|
const fieldNames = convertToArrayPayload(name);
|
|
2864
2908
|
if (_options.resolver) {
|
|
2909
|
+
const resetCallId = _resetCallId;
|
|
2865
2910
|
const errors = await executeSchemaAndUpdateState(isUndefined(name) ? name : fieldNames);
|
|
2866
2911
|
isValid = isEmptyObject(errors);
|
|
2867
2912
|
validationResult = name
|
|
2868
2913
|
? !fieldNames.some((name) => get(errors, name))
|
|
2869
2914
|
: isValid;
|
|
2915
|
+
if (resetCallId !== _resetCallId) {
|
|
2916
|
+
return validationResult;
|
|
2917
|
+
}
|
|
2870
2918
|
}
|
|
2871
2919
|
else if (name) {
|
|
2872
2920
|
validationResult = (await Promise.all(fieldNames.map(async (fieldName) => {
|
|
@@ -2893,8 +2941,7 @@ function createFormControl(props = {}) {
|
|
|
2893
2941
|
delayErrorCallbacks[name](_options.delayError);
|
|
2894
2942
|
}
|
|
2895
2943
|
else {
|
|
2896
|
-
|
|
2897
|
-
delete delayErrorCallbacks[name];
|
|
2944
|
+
cancelDelayedError(name);
|
|
2898
2945
|
}
|
|
2899
2946
|
}
|
|
2900
2947
|
if (options.shouldTouch) {
|
|
@@ -2905,13 +2952,11 @@ function createFormControl(props = {}) {
|
|
|
2905
2952
|
}
|
|
2906
2953
|
_subjects.state.next({
|
|
2907
2954
|
...(!isString(name) ||
|
|
2908
|
-
((
|
|
2909
|
-
isValid !== _formState.isValid)
|
|
2955
|
+
(_isTracked('isValid') && isValid !== _formState.isValid)
|
|
2910
2956
|
? {}
|
|
2911
2957
|
: { name }),
|
|
2912
2958
|
...(_options.resolver || !name ? { isValid } : {}),
|
|
2913
|
-
...(options.shouldTouch &&
|
|
2914
|
-
(_proxyFormState.touchedFields || _proxySubscribeFormState.touchedFields)
|
|
2959
|
+
...(options.shouldTouch && _isTracked('touchedFields')
|
|
2915
2960
|
? { touchedFields: _formState.touchedFields }
|
|
2916
2961
|
: {}),
|
|
2917
2962
|
errors: _formState.errors,
|
|
@@ -2946,15 +2991,16 @@ function createFormControl(props = {}) {
|
|
|
2946
2991
|
invalid: !!error,
|
|
2947
2992
|
isDirty: !!get(targetFormState.dirtyFields, name),
|
|
2948
2993
|
error,
|
|
2949
|
-
isValidating: !!get(
|
|
2994
|
+
isValidating: !!get(targetFormState.validatingFields, name),
|
|
2950
2995
|
isTouched: !!get(targetFormState.touchedFields, name),
|
|
2951
2996
|
};
|
|
2952
2997
|
};
|
|
2953
2998
|
const clearErrors = (name) => {
|
|
2954
2999
|
const names = name ? convertToArrayPayload(name) : undefined;
|
|
2955
|
-
names === null || names === void 0 ? void 0 : names.forEach((inputName) => unset(_formState.errors, inputName));
|
|
2956
3000
|
if (names) {
|
|
2957
3001
|
names.forEach((inputName) => {
|
|
3002
|
+
cancelDelayedErrorTree(inputName);
|
|
3003
|
+
unset(_formState.errors, inputName);
|
|
2958
3004
|
_subjects.state.next({
|
|
2959
3005
|
name: inputName,
|
|
2960
3006
|
errors: _formState.errors,
|
|
@@ -2962,6 +3008,7 @@ function createFormControl(props = {}) {
|
|
|
2962
3008
|
});
|
|
2963
3009
|
}
|
|
2964
3010
|
else {
|
|
3011
|
+
Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
|
|
2965
3012
|
_formState.errors = {};
|
|
2966
3013
|
_subjects.state.next({
|
|
2967
3014
|
errors: _formState.errors,
|
|
@@ -2969,9 +3016,10 @@ function createFormControl(props = {}) {
|
|
|
2969
3016
|
}
|
|
2970
3017
|
};
|
|
2971
3018
|
const setError = (name, error, options) => {
|
|
3019
|
+
cancelDelayedErrorTree(name);
|
|
2972
3020
|
const ref = (get(_fields, name, { _f: {} })._f || {}).ref;
|
|
2973
3021
|
const currentError = get(_formState.errors, name) || {};
|
|
2974
|
-
const { ref: currentRef, message, type, ...restOfErrorTree } = currentError;
|
|
3022
|
+
const { ref: currentRef, message, type, types, ...restOfErrorTree } = currentError;
|
|
2975
3023
|
set(_formState.errors, name, {
|
|
2976
3024
|
...restOfErrorTree,
|
|
2977
3025
|
...error,
|
|
@@ -3056,11 +3104,15 @@ function createFormControl(props = {}) {
|
|
|
3056
3104
|
for (const fieldName of name ? convertToArrayPayload(name) : _names.mount) {
|
|
3057
3105
|
_names.mount.delete(fieldName);
|
|
3058
3106
|
_names.array.delete(fieldName);
|
|
3107
|
+
_names.disabled.delete(fieldName);
|
|
3059
3108
|
if (!options.keepValue) {
|
|
3060
3109
|
unset(_fields, fieldName);
|
|
3061
3110
|
unset(_formValues, fieldName);
|
|
3062
3111
|
}
|
|
3063
|
-
!options.keepError
|
|
3112
|
+
if (!options.keepError) {
|
|
3113
|
+
cancelDelayedErrorTree(fieldName);
|
|
3114
|
+
unset(_formState.errors, fieldName);
|
|
3115
|
+
}
|
|
3064
3116
|
!options.keepDirty && unset(_formState.dirtyFields, fieldName);
|
|
3065
3117
|
!options.keepTouched && unset(_formState.touchedFields, fieldName);
|
|
3066
3118
|
!options.keepIsValidating &&
|
|
@@ -3076,6 +3128,9 @@ function createFormControl(props = {}) {
|
|
|
3076
3128
|
_subjects.state.next({
|
|
3077
3129
|
..._formState,
|
|
3078
3130
|
...(options.keepDirty ? {} : { isDirty: _getDirty() }),
|
|
3131
|
+
...(options.keepIsValidating
|
|
3132
|
+
? {}
|
|
3133
|
+
: { isValidating: !isEmptyObject(_formState.validatingFields) }),
|
|
3079
3134
|
});
|
|
3080
3135
|
!options.keepIsValid && _setValid();
|
|
3081
3136
|
};
|
|
@@ -3104,6 +3159,14 @@ function createFormControl(props = {}) {
|
|
|
3104
3159
|
},
|
|
3105
3160
|
});
|
|
3106
3161
|
_names.mount.add(name);
|
|
3162
|
+
if (field && field._f) {
|
|
3163
|
+
const nextField = get(_fields, name);
|
|
3164
|
+
for (const rule of REGISTER_VALIDATION_RULES) {
|
|
3165
|
+
if (!(rule in options)) {
|
|
3166
|
+
delete nextField._f[rule];
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3107
3170
|
if (field && !shouldRevalidateRemount) {
|
|
3108
3171
|
_setDisabledField({
|
|
3109
3172
|
disabled: isBoolean(options.disabled)
|
|
@@ -3198,7 +3261,7 @@ function createFormControl(props = {}) {
|
|
|
3198
3261
|
});
|
|
3199
3262
|
}
|
|
3200
3263
|
}
|
|
3201
|
-
}, 0
|
|
3264
|
+
}, 0);
|
|
3202
3265
|
}
|
|
3203
3266
|
};
|
|
3204
3267
|
const handleSubmit = (onValid, onInvalid) => async (e) => {
|
|
@@ -3214,23 +3277,32 @@ function createFormControl(props = {}) {
|
|
|
3214
3277
|
isSubmitting: true,
|
|
3215
3278
|
});
|
|
3216
3279
|
if (_options.resolver) {
|
|
3280
|
+
const resetCallId = _resetCallId;
|
|
3217
3281
|
const { errors, values } = await _runSchema();
|
|
3282
|
+
if (resetCallId !== _resetCallId) {
|
|
3283
|
+
return;
|
|
3284
|
+
}
|
|
3218
3285
|
_updateIsValidating();
|
|
3286
|
+
Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
|
|
3219
3287
|
_formState.errors = errors;
|
|
3220
3288
|
fieldValues = cloneObject(values);
|
|
3221
3289
|
}
|
|
3222
3290
|
else {
|
|
3291
|
+
const resetCallId = _resetCallId;
|
|
3223
3292
|
await executeBuiltInValidation({
|
|
3224
3293
|
fields: _fields,
|
|
3225
3294
|
eventType: EVENTS.SUBMIT,
|
|
3226
3295
|
});
|
|
3296
|
+
if (resetCallId !== _resetCallId) {
|
|
3297
|
+
return;
|
|
3298
|
+
}
|
|
3299
|
+
unset(_formState.errors, ROOT_ERROR_TYPE);
|
|
3227
3300
|
}
|
|
3228
3301
|
if (_names.disabled.size) {
|
|
3229
3302
|
for (const name of _names.disabled) {
|
|
3230
3303
|
unset(fieldValues, name);
|
|
3231
3304
|
}
|
|
3232
3305
|
}
|
|
3233
|
-
unset(_formState.errors, ROOT_ERROR_TYPE);
|
|
3234
3306
|
if (isEmptyObject(_formState.errors)) {
|
|
3235
3307
|
_subjects.state.next({
|
|
3236
3308
|
errors: {},
|
|
@@ -3263,6 +3335,7 @@ function createFormControl(props = {}) {
|
|
|
3263
3335
|
};
|
|
3264
3336
|
const resetField = (name, options = {}) => {
|
|
3265
3337
|
if (get(_fields, name)) {
|
|
3338
|
+
unset(_formState.validatingFields, name);
|
|
3266
3339
|
if (isUndefined(options.defaultValue)) {
|
|
3267
3340
|
setValue(name, cloneObject(get(_defaultValues, name)));
|
|
3268
3341
|
}
|
|
@@ -3280,18 +3353,24 @@ function createFormControl(props = {}) {
|
|
|
3280
3353
|
: _getDirty();
|
|
3281
3354
|
}
|
|
3282
3355
|
if (!options.keepError) {
|
|
3356
|
+
cancelDelayedErrorTree(name);
|
|
3283
3357
|
unset(_formState.errors, name);
|
|
3284
3358
|
_setValid();
|
|
3285
3359
|
}
|
|
3286
|
-
_subjects.state.next({
|
|
3360
|
+
_subjects.state.next({
|
|
3361
|
+
..._formState,
|
|
3362
|
+
isValidating: !isEmptyObject(_formState.validatingFields),
|
|
3363
|
+
});
|
|
3287
3364
|
}
|
|
3288
3365
|
};
|
|
3289
3366
|
const _reset = (formValues, keepStateOptions = {}) => {
|
|
3367
|
+
_resetCallId++;
|
|
3290
3368
|
const updatedValues = formValues ? cloneObject(formValues) : _defaultValues;
|
|
3291
3369
|
const cloneUpdatedValues = cloneObject(updatedValues);
|
|
3292
3370
|
const isEmptyResetValues = isEmptyObject(formValues);
|
|
3293
3371
|
const values = cloneUpdatedValues;
|
|
3294
3372
|
const fieldRefs = _fields;
|
|
3373
|
+
Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
|
|
3295
3374
|
if (!keepStateOptions.keepDefaultValues) {
|
|
3296
3375
|
_defaultValues = updatedValues;
|
|
3297
3376
|
}
|
|
@@ -3409,10 +3488,16 @@ function createFormControl(props = {}) {
|
|
|
3409
3488
|
? getDirtyFields(_defaultValues, formValues, undefined, fieldRefs)
|
|
3410
3489
|
: keepStateOptions.keepDirty
|
|
3411
3490
|
? _formState.dirtyFields
|
|
3412
|
-
:
|
|
3491
|
+
: keepStateOptions.keepValues
|
|
3492
|
+
? getDirtyFields(_defaultValues, _formValues, undefined, fieldRefs)
|
|
3493
|
+
: {},
|
|
3413
3494
|
touchedFields: keepStateOptions.keepTouched
|
|
3414
3495
|
? _formState.touchedFields
|
|
3415
3496
|
: {},
|
|
3497
|
+
...(!keepStateOptions.keepIsValidating &&
|
|
3498
|
+
(_formState.isValidating || !isEmptyObject(_formState.validatingFields))
|
|
3499
|
+
? { validatingFields: {}, isValidating: false }
|
|
3500
|
+
: null),
|
|
3416
3501
|
errors: keepStateOptions.keepErrors ? _formState.errors : {},
|
|
3417
3502
|
isSubmitSuccessful: keepStateOptions.keepIsSubmitSuccessful
|
|
3418
3503
|
? _formState.isSubmitSuccessful
|
|
@@ -3534,6 +3619,8 @@ function createFormControl(props = {}) {
|
|
|
3534
3619
|
};
|
|
3535
3620
|
_validationModeBeforeSubmit = getValidationModes(_options.mode);
|
|
3536
3621
|
_validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
|
|
3622
|
+
shouldDisplayAllAssociatedErrors =
|
|
3623
|
+
_options.criteriaMode === VALIDATION_MODE.all;
|
|
3537
3624
|
},
|
|
3538
3625
|
},
|
|
3539
3626
|
subscribe,
|
|
@@ -3561,38 +3648,24 @@ function createFormControl(props = {}) {
|
|
|
3561
3648
|
}
|
|
3562
3649
|
|
|
3563
3650
|
/**
|
|
3564
|
-
*
|
|
3565
|
-
*
|
|
3566
|
-
* @remarks
|
|
3567
|
-
* [API](https://react-hook-form.com/docs/useform) • [Demo](https://codesandbox.io/s/react-hook-form-get-started-ts-5ksmm) • [Video](https://www.youtube.com/watch?v=RkXv4AXXC_4)
|
|
3568
|
-
*
|
|
3569
|
-
* @param props - form configuration and validation parameters.
|
|
3651
|
+
* Core hook for managing a form. Returns all methods and state for
|
|
3652
|
+
* registration, validation, and submission.
|
|
3570
3653
|
*
|
|
3571
|
-
* @
|
|
3654
|
+
* @see [API](https://react-hook-form.com/docs/useform)
|
|
3572
3655
|
*
|
|
3573
3656
|
* @example
|
|
3574
3657
|
* ```tsx
|
|
3575
|
-
*
|
|
3576
|
-
*
|
|
3577
|
-
*
|
|
3578
|
-
*
|
|
3579
|
-
* console.log(watch("example"));
|
|
3580
|
-
*
|
|
3581
|
-
* return (
|
|
3582
|
-
* <form onSubmit={handleSubmit(onSubmit)}>
|
|
3583
|
-
* <input defaultValue="test" {...register("example")} />
|
|
3584
|
-
* <input {...register("exampleRequired", { required: true })} />
|
|
3585
|
-
* {errors.exampleRequired && <span>This field is required</span>}
|
|
3586
|
-
* <button>Submit</button>
|
|
3587
|
-
* </form>
|
|
3588
|
-
* );
|
|
3589
|
-
* }
|
|
3658
|
+
* const { register, handleSubmit, formState: { errors } } = useForm<FormValues>();
|
|
3659
|
+
* <form onSubmit={handleSubmit(onSubmit)}>
|
|
3660
|
+
* <input {...register("email", { required: true })} />
|
|
3661
|
+
* </form>
|
|
3590
3662
|
* ```
|
|
3591
3663
|
*/
|
|
3592
3664
|
function useForm(props = {}) {
|
|
3593
3665
|
const _formControl = React.useRef(undefined);
|
|
3594
3666
|
const _values = React.useRef(undefined);
|
|
3595
3667
|
const _formControlProp = React.useRef(props.formControl);
|
|
3668
|
+
const _hadValidate = React.useRef(!!props.validate);
|
|
3596
3669
|
const [formState, updateFormState] = React.useState(() => ({
|
|
3597
3670
|
...cloneObject(DEFAULT_FORM_STATE),
|
|
3598
3671
|
isLoading: isFunction(props.defaultValues),
|
|
@@ -3623,13 +3696,13 @@ function useForm(props = {}) {
|
|
|
3623
3696
|
}
|
|
3624
3697
|
}
|
|
3625
3698
|
const control = _formControl.current.control;
|
|
3626
|
-
control._options = props;
|
|
3627
|
-
const
|
|
3699
|
+
control._options = { ...props, validate: props.validate };
|
|
3700
|
+
const getCurrentFormState = () => ({
|
|
3701
|
+
...control._formState,
|
|
3702
|
+
defaultValues: control._defaultValues,
|
|
3703
|
+
});
|
|
3704
|
+
const { resyncIfNeeded, snapshot } = useResyncOnReconnect(getCurrentFormState);
|
|
3628
3705
|
useIsomorphicLayoutEffect(() => {
|
|
3629
|
-
const getCurrentFormState = () => ({
|
|
3630
|
-
...control._formState,
|
|
3631
|
-
defaultValues: control._defaultValues,
|
|
3632
|
-
});
|
|
3633
3706
|
resyncIfNeeded(true, getCurrentFormState, updateFormState);
|
|
3634
3707
|
const unsubscribe = control._subscribe({
|
|
3635
3708
|
formState: control._proxyFormState,
|
|
@@ -3664,6 +3737,13 @@ function useForm(props = {}) {
|
|
|
3664
3737
|
control._focusError();
|
|
3665
3738
|
}
|
|
3666
3739
|
}, [control, props.errors]);
|
|
3740
|
+
React.useEffect(() => {
|
|
3741
|
+
var _a;
|
|
3742
|
+
if (_hadValidate.current && !props.validate) {
|
|
3743
|
+
(_a = _formControl.current) === null || _a === void 0 ? void 0 : _a.clearErrors(FORM_ERROR_TYPE);
|
|
3744
|
+
}
|
|
3745
|
+
_hadValidate.current = !!props.validate;
|
|
3746
|
+
}, [props.validate]);
|
|
3667
3747
|
React.useEffect(() => {
|
|
3668
3748
|
props.shouldUnregister &&
|
|
3669
3749
|
control._subjects.state.next({
|
|
@@ -3713,32 +3793,20 @@ function useForm(props = {}) {
|
|
|
3713
3793
|
}
|
|
3714
3794
|
|
|
3715
3795
|
/**
|
|
3716
|
-
*
|
|
3796
|
+
* Component wrapper around `useWatch`. Re-renders only when watched fields change.
|
|
3717
3797
|
*
|
|
3718
|
-
* @
|
|
3719
|
-
* @param name - Can be field name, array of field names, or undefined to watch the entire form
|
|
3720
|
-
* @param disabled - Disable subscription
|
|
3721
|
-
* @param exact - Whether to watch exact field names or not
|
|
3722
|
-
* @param defaultValue - The default value to use if the field is not yet set
|
|
3723
|
-
* @param compute - Function to compute derived values from watched fields
|
|
3724
|
-
* @param render - The function that receives watched values and returns ReactNode
|
|
3725
|
-
* @returns The result of calling render function with watched values
|
|
3798
|
+
* @see [API](https://react-hook-form.com/docs/usewatch)
|
|
3726
3799
|
*
|
|
3727
3800
|
* @example
|
|
3728
|
-
* The `Watch` component only re-render when the values of `foo`, `bar`, and `baz.qux` change.
|
|
3729
|
-
* The types of `foo`, `bar`, and `baz.qux` are precisely inferred.
|
|
3730
|
-
*
|
|
3731
3801
|
* ```tsx
|
|
3732
|
-
* const { control } = useForm();
|
|
3733
|
-
*
|
|
3734
3802
|
* <Watch
|
|
3735
3803
|
* control={control}
|
|
3736
|
-
* names={[
|
|
3737
|
-
* render={([foo, bar
|
|
3804
|
+
* names={["foo", "bar"]}
|
|
3805
|
+
* render={([foo, bar]) => <span>{foo} {bar}</span>}
|
|
3738
3806
|
* />
|
|
3739
3807
|
* ```
|
|
3740
3808
|
*/
|
|
3741
3809
|
const Watch = (props) => props.render(useWatch({ name: props.names, ...props }));
|
|
3742
3810
|
|
|
3743
|
-
export { Controller, FieldArray, Form, FormProvider, FormState, FormStateSubscribe, Watch, appendErrors, createFormControl, get, set, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch };
|
|
3811
|
+
export { Controller, ErrorMessage, FieldArray, Form, FormProvider, FormState, FormStateSubscribe, Watch, appendErrors, createFormControl, get, set, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch };
|
|
3744
3812
|
//# sourceMappingURL=index.esm.mjs.map
|