react-hook-form 7.87.0 → 7.88.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/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 +262 -295
- 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/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 +2464 -0
- package/dist/react-server.esm.mjs.map +1 -0
- 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 +2 -1
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);
|
|
@@ -274,33 +275,14 @@ function useResyncOnReconnect(getInitialValue) {
|
|
|
274
275
|
}
|
|
275
276
|
|
|
276
277
|
/**
|
|
277
|
-
* Subscribes to
|
|
278
|
+
* Subscribes to form state with re-renders isolated to this hook.
|
|
279
|
+
* Optionally scope to specific field names to minimize re-render surface.
|
|
278
280
|
*
|
|
279
|
-
* @
|
|
280
|
-
* [API](https://react-hook-form.com/docs/useformstate) • [Demo](https://codesandbox.io/s/useformstate-75xly)
|
|
281
|
-
*
|
|
282
|
-
* @param props - Include options to specify fields to subscribe to. {@link UseFormStateReturn}
|
|
281
|
+
* @see [API](https://react-hook-form.com/docs/useformstate)
|
|
283
282
|
*
|
|
284
283
|
* @example
|
|
285
284
|
* ```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
|
-
* }
|
|
285
|
+
* const { errors, isDirty } = useFormState({ control, name: "email" });
|
|
304
286
|
* ```
|
|
305
287
|
*/
|
|
306
288
|
function useFormState(props) {
|
|
@@ -341,7 +323,7 @@ function useFormState(props) {
|
|
|
341
323
|
unsubscribe();
|
|
342
324
|
snapshot(!disabled, getCurrentFormState);
|
|
343
325
|
};
|
|
344
|
-
}, [name, disabled, exact, resyncIfNeeded, snapshot]);
|
|
326
|
+
}, [control, name, disabled, exact, resyncIfNeeded, snapshot]);
|
|
345
327
|
React.useEffect(() => {
|
|
346
328
|
_localProxyFormState.current.isValid && control._setValid(true);
|
|
347
329
|
}, [control]);
|
|
@@ -364,19 +346,15 @@ var generateWatchOutput = (names, _names, formValues, isGlobal, defaultValue) =>
|
|
|
364
346
|
};
|
|
365
347
|
|
|
366
348
|
/**
|
|
367
|
-
*
|
|
349
|
+
* Subscribes to field value changes and isolates re-renders to the hook level.
|
|
368
350
|
*
|
|
369
|
-
* @
|
|
370
|
-
*
|
|
371
|
-
* [API](https://react-hook-form.com/docs/usewatch) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-usewatch-h9i5e)
|
|
351
|
+
* @see [API](https://react-hook-form.com/docs/usewatch)
|
|
372
352
|
*
|
|
373
353
|
* @example
|
|
374
354
|
* ```tsx
|
|
375
|
-
* const { control
|
|
376
|
-
* const
|
|
377
|
-
*
|
|
378
|
-
* control,
|
|
379
|
-
* })
|
|
355
|
+
* const email = useWatch({ control, name: "email" });
|
|
356
|
+
* const all = useWatch({ control });
|
|
357
|
+
* const adult = useWatch({ control, name: "age", compute: (v) => v >= 18 });
|
|
380
358
|
* ```
|
|
381
359
|
*/
|
|
382
360
|
function useWatch(props) {
|
|
@@ -384,7 +362,6 @@ function useWatch(props) {
|
|
|
384
362
|
const { control = formControl, name, defaultValue, disabled, exact, compute, } = props || {};
|
|
385
363
|
const _defaultValue = React.useRef(defaultValue);
|
|
386
364
|
const _compute = React.useRef(compute);
|
|
387
|
-
const _computeFormValues = React.useRef(undefined);
|
|
388
365
|
const _prevControl = React.useRef(control);
|
|
389
366
|
const _prevName = React.useRef(name);
|
|
390
367
|
_compute.current = compute;
|
|
@@ -393,6 +370,7 @@ function useWatch(props) {
|
|
|
393
370
|
return _compute.current ? _compute.current(defaultValue) : defaultValue;
|
|
394
371
|
};
|
|
395
372
|
const [value, updateValue] = React.useState(getInitialOutput);
|
|
373
|
+
const _computeFormValues = React.useRef(value);
|
|
396
374
|
const getCurrentOutput = React.useCallback((values) => {
|
|
397
375
|
const formValues = generateWatchOutput(name, control._names, values || control._formValues, false, _defaultValue.current);
|
|
398
376
|
return _compute.current ? _compute.current(formValues) : formValues;
|
|
@@ -466,27 +444,15 @@ function useWatch(props) {
|
|
|
466
444
|
}
|
|
467
445
|
|
|
468
446
|
/**
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
* @remarks
|
|
472
|
-
* [API](https://react-hook-form.com/docs/usecontroller) • [Demo](https://codesandbox.io/s/usecontroller-0o8px)
|
|
447
|
+
* Hook for controlled inputs. Returns `field`, `fieldState`, and `formState`.
|
|
448
|
+
* Re-renders are isolated to the hook level.
|
|
473
449
|
*
|
|
474
|
-
* @
|
|
475
|
-
*
|
|
476
|
-
* @returns field properties, field and form state. {@link UseControllerReturn}
|
|
450
|
+
* @see [API](https://react-hook-form.com/docs/usecontroller)
|
|
477
451
|
*
|
|
478
452
|
* @example
|
|
479
453
|
* ```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
|
-
* }
|
|
454
|
+
* const { field, fieldState } = useController({ control, name: "email" });
|
|
455
|
+
* return <input {...field} />;
|
|
490
456
|
* ```
|
|
491
457
|
*/
|
|
492
458
|
function useController(props) {
|
|
@@ -640,48 +606,50 @@ function useController(props) {
|
|
|
640
606
|
}
|
|
641
607
|
|
|
642
608
|
/**
|
|
643
|
-
* Component
|
|
609
|
+
* Component wrapper around `useController` for controlled inputs.
|
|
644
610
|
*
|
|
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)
|
|
611
|
+
* @see [API](https://react-hook-form.com/docs/usecontroller/controller)
|
|
647
612
|
*
|
|
648
|
-
* @
|
|
613
|
+
* @example
|
|
614
|
+
* ```tsx
|
|
615
|
+
* <Controller
|
|
616
|
+
* control={control}
|
|
617
|
+
* name="test"
|
|
618
|
+
* render={({ field, fieldState, formState }) => <input {...field} />}
|
|
619
|
+
* />
|
|
620
|
+
* ```
|
|
621
|
+
*/
|
|
622
|
+
const Controller = (props) => props.render(useController(props));
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Displays the validation error for a single field.
|
|
626
|
+
* Reads from `control` when provided, otherwise from the nearest `FormProvider`.
|
|
649
627
|
*
|
|
650
|
-
* @
|
|
628
|
+
* @see [API](https://react-hook-form.com/docs/useformstate/errormessage)
|
|
651
629
|
*
|
|
652
630
|
* @example
|
|
653
631
|
* ```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
|
-
* }
|
|
632
|
+
* <ErrorMessage control={control} name="email" as="p" />
|
|
633
|
+
* <ErrorMessage name="email" as="span" />
|
|
634
|
+
* <ErrorMessage control={control} name="email"
|
|
635
|
+
* render={({ message }) => <Alert>{message}</Alert>} />
|
|
682
636
|
* ```
|
|
683
637
|
*/
|
|
684
|
-
const
|
|
638
|
+
const ErrorMessage = ({ as, control, name, render, }) => {
|
|
639
|
+
const { errors } = useFormState({
|
|
640
|
+
control,
|
|
641
|
+
name: name,
|
|
642
|
+
});
|
|
643
|
+
const error = get(errors, name);
|
|
644
|
+
if (!error) {
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
const message = error.message || '';
|
|
648
|
+
if (render) {
|
|
649
|
+
return render({ message, messages: error.types });
|
|
650
|
+
}
|
|
651
|
+
return React.createElement(as || React.Fragment, null, message);
|
|
652
|
+
};
|
|
685
653
|
|
|
686
654
|
var generateId = () => typeof crypto !== 'undefined' && crypto.randomUUID
|
|
687
655
|
? crypto.randomUUID()
|
|
@@ -715,7 +683,7 @@ var isWatched = (name, _names, isBlurEvent) => {
|
|
|
715
683
|
return false;
|
|
716
684
|
};
|
|
717
685
|
|
|
718
|
-
const iterateFieldsByAction = (fields, action, fieldsNames
|
|
686
|
+
const iterateFieldsByAction = (fields, action, fieldsNames) => {
|
|
719
687
|
for (const key of fieldsNames || Object.keys(fields)) {
|
|
720
688
|
if (key === '_f') {
|
|
721
689
|
continue;
|
|
@@ -724,21 +692,21 @@ const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
|
|
|
724
692
|
if (field) {
|
|
725
693
|
const { _f } = field;
|
|
726
694
|
if (_f) {
|
|
727
|
-
if (_f.refs && _f.refs[0] && action(_f.refs[0],
|
|
695
|
+
if (_f.refs && _f.refs[0] && action(_f.refs[0], _f.name)) {
|
|
728
696
|
return true;
|
|
729
697
|
}
|
|
730
|
-
else if (_f.ref && action(_f.ref, _f.name)
|
|
698
|
+
else if (_f.ref && action(_f.ref, _f.name)) {
|
|
731
699
|
return true;
|
|
732
700
|
}
|
|
733
701
|
else {
|
|
734
702
|
if (iterateFieldsByAction(field, action)) {
|
|
735
|
-
|
|
703
|
+
return true;
|
|
736
704
|
}
|
|
737
705
|
}
|
|
738
706
|
}
|
|
739
707
|
else if (isObject(field) || Array.isArray(field)) {
|
|
740
708
|
if (iterateFieldsByAction(field, action)) {
|
|
741
|
-
|
|
709
|
+
return true;
|
|
742
710
|
}
|
|
743
711
|
}
|
|
744
712
|
}
|
|
@@ -1004,7 +972,9 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
|
|
|
1004
972
|
...validateError,
|
|
1005
973
|
...appendErrorsCurry(key, validateError.message),
|
|
1006
974
|
};
|
|
1007
|
-
|
|
975
|
+
if (!validateAllFieldCriteria) {
|
|
976
|
+
setCustomValidity(validateError.message);
|
|
977
|
+
}
|
|
1008
978
|
if (validateAllFieldCriteria) {
|
|
1009
979
|
error[name] = validationResult;
|
|
1010
980
|
}
|
|
@@ -1132,48 +1102,27 @@ var updateAt = (fieldValues, index, value) => {
|
|
|
1132
1102
|
};
|
|
1133
1103
|
|
|
1134
1104
|
/**
|
|
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
|
|
1105
|
+
* Hook for dynamic field arrays. Provides `fields` and mutation methods:
|
|
1106
|
+
* `append`, `prepend`, `remove`, `insert`, `swap`, `move`, `update`, `replace`.
|
|
1141
1107
|
*
|
|
1142
|
-
* @
|
|
1108
|
+
* @see [API](https://react-hook-form.com/docs/usefieldarray)
|
|
1143
1109
|
*
|
|
1144
1110
|
* @example
|
|
1145
1111
|
* ```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
|
-
* }
|
|
1112
|
+
* const { fields, append } = useFieldArray({ control, name: "items" });
|
|
1113
|
+
* return fields.map((f, i) => <input key={f.id} {...register(`items.${i}.name`)} />);
|
|
1169
1114
|
* ```
|
|
1170
1115
|
*/
|
|
1171
1116
|
function useFieldArray(props) {
|
|
1172
1117
|
const formControl = useFormControlContext();
|
|
1173
1118
|
const { control = formControl, name, keyName = 'id', disabled, shouldUnregister, rules, } = props;
|
|
1174
|
-
const
|
|
1119
|
+
const getCurrentFieldArray = () => control._getFieldArray(name);
|
|
1120
|
+
const [fields, setFields] = React.useState(getCurrentFieldArray);
|
|
1175
1121
|
const ids = React.useRef(control._getFieldArray(name).map(generateId));
|
|
1176
1122
|
const _actioned = React.useRef(false);
|
|
1123
|
+
const { resyncIfNeeded, snapshot } = useResyncOnReconnect(getCurrentFieldArray);
|
|
1124
|
+
const _prevControl = React.useRef(control);
|
|
1125
|
+
const _prevName = React.useRef(name);
|
|
1177
1126
|
if (!disabled) {
|
|
1178
1127
|
control._names.array.add(name);
|
|
1179
1128
|
}
|
|
@@ -1185,7 +1134,23 @@ function useFieldArray(props) {
|
|
|
1185
1134
|
if (disabled) {
|
|
1186
1135
|
return;
|
|
1187
1136
|
}
|
|
1188
|
-
|
|
1137
|
+
if (_prevControl.current === control && _prevName.current === name) {
|
|
1138
|
+
resyncIfNeeded(true, getCurrentFieldArray, (fieldValues) => {
|
|
1139
|
+
setFields(fieldValues);
|
|
1140
|
+
ids.current = fieldValues.map(generateId);
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
else {
|
|
1144
|
+
_prevControl.current = control;
|
|
1145
|
+
_prevName.current = name;
|
|
1146
|
+
const fieldValues = getCurrentFieldArray();
|
|
1147
|
+
if (!deepEqual(fields, fieldValues)) {
|
|
1148
|
+
setFields(fieldValues);
|
|
1149
|
+
ids.current = fieldValues.map(generateId);
|
|
1150
|
+
}
|
|
1151
|
+
snapshot(true, getCurrentFieldArray);
|
|
1152
|
+
}
|
|
1153
|
+
const unsubscribe = control._subjects.array.subscribe({
|
|
1189
1154
|
next: ({ values, name: fieldArrayName, }) => {
|
|
1190
1155
|
if (fieldArrayName === name || !fieldArrayName) {
|
|
1191
1156
|
const fieldValues = get(values, name);
|
|
@@ -1200,7 +1165,11 @@ function useFieldArray(props) {
|
|
|
1200
1165
|
}
|
|
1201
1166
|
},
|
|
1202
1167
|
}).unsubscribe;
|
|
1203
|
-
|
|
1168
|
+
return () => {
|
|
1169
|
+
unsubscribe();
|
|
1170
|
+
snapshot(true, getCurrentFieldArray);
|
|
1171
|
+
};
|
|
1172
|
+
}, [control, name, disabled, resyncIfNeeded, snapshot]);
|
|
1204
1173
|
const updateValues = React.useCallback((updatedFieldArrayValues) => {
|
|
1205
1174
|
_actioned.current = true;
|
|
1206
1175
|
control._setFieldArray(name, updatedFieldArrayValues);
|
|
@@ -1312,7 +1281,9 @@ function useFieldArray(props) {
|
|
|
1312
1281
|
ids.current = updatedFieldArrayValues.map(generateId);
|
|
1313
1282
|
updateValues([...updatedFieldArrayValues]);
|
|
1314
1283
|
setFields([...updatedFieldArrayValues]);
|
|
1315
|
-
control._setFieldArray(name, [...updatedFieldArrayValues], (data) => data
|
|
1284
|
+
control._setFieldArray(name, [...updatedFieldArrayValues], (data) => Array.isArray(data)
|
|
1285
|
+
? data.slice(0, updatedFieldArrayValues.length)
|
|
1286
|
+
: data, {});
|
|
1316
1287
|
};
|
|
1317
1288
|
React.useEffect(() => {
|
|
1318
1289
|
if (disabled) {
|
|
@@ -1482,13 +1453,15 @@ const FieldArray = (props) => props.render(useFieldArray(props));
|
|
|
1482
1453
|
|
|
1483
1454
|
const isFileLike = (value) => (typeof Blob !== 'undefined' && value instanceof Blob) ||
|
|
1484
1455
|
(typeof File !== 'undefined' && value instanceof File);
|
|
1456
|
+
const isFileListLike = (value) => typeof FileList !== 'undefined' && value instanceof FileList;
|
|
1485
1457
|
const flatten = (obj) => {
|
|
1486
1458
|
const output = {};
|
|
1487
1459
|
for (const key of Object.keys(obj)) {
|
|
1488
1460
|
if (isObjectType(obj[key]) &&
|
|
1489
1461
|
obj[key] !== null &&
|
|
1490
1462
|
!isDateObject(obj[key]) &&
|
|
1491
|
-
!isFileLike(obj[key])
|
|
1463
|
+
!isFileLike(obj[key]) &&
|
|
1464
|
+
!isFileListLike(obj[key])) {
|
|
1492
1465
|
const nested = flatten(obj[key]);
|
|
1493
1466
|
for (const nestedKey of Object.keys(nested)) {
|
|
1494
1467
|
output[`${key}.${nestedKey}`] = nested[nestedKey];
|
|
@@ -1505,7 +1478,18 @@ function jsonToFormData(json) {
|
|
|
1505
1478
|
const result = new FormData();
|
|
1506
1479
|
const flattenFormValues = flatten(json);
|
|
1507
1480
|
for (const key in flattenFormValues) {
|
|
1508
|
-
|
|
1481
|
+
const value = flattenFormValues[key];
|
|
1482
|
+
if (isUndefined(value)) {
|
|
1483
|
+
continue;
|
|
1484
|
+
}
|
|
1485
|
+
if (typeof FileList !== 'undefined' && value instanceof FileList) {
|
|
1486
|
+
for (let index = 0; index < value.length; index++) {
|
|
1487
|
+
const file = value[index];
|
|
1488
|
+
file && result.append(key, file);
|
|
1489
|
+
}
|
|
1490
|
+
continue;
|
|
1491
|
+
}
|
|
1492
|
+
result.append(key, value);
|
|
1509
1493
|
}
|
|
1510
1494
|
return result;
|
|
1511
1495
|
}
|
|
@@ -1524,64 +1508,28 @@ function noop() { }
|
|
|
1524
1508
|
const HookFormContext = React.createContext(null);
|
|
1525
1509
|
HookFormContext.displayName = 'HookFormContext';
|
|
1526
1510
|
/**
|
|
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)
|
|
1511
|
+
* Retrieves all `useForm` methods from the nearest `FormProvider`.
|
|
1512
|
+
* Use in deeply nested components to avoid prop-drilling.
|
|
1531
1513
|
*
|
|
1532
|
-
* @
|
|
1514
|
+
* @see [API](https://react-hook-form.com/docs/useformcontext)
|
|
1533
1515
|
*
|
|
1534
1516
|
* @example
|
|
1535
1517
|
* ```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
|
-
* }
|
|
1518
|
+
* const { register } = useFormContext<FormValues>();
|
|
1554
1519
|
* ```
|
|
1555
1520
|
*/
|
|
1556
1521
|
const useFormContext = () => React.useContext(HookFormContext);
|
|
1557
1522
|
/**
|
|
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)
|
|
1523
|
+
* Provides all `useForm` methods to the component tree via React Context.
|
|
1524
|
+
* Pair with `useFormContext` to consume them in any descendant.
|
|
1562
1525
|
*
|
|
1563
|
-
* @
|
|
1526
|
+
* @see [API](https://react-hook-form.com/docs/useformcontext)
|
|
1564
1527
|
*
|
|
1565
1528
|
* @example
|
|
1566
1529
|
* ```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
|
-
* }
|
|
1530
|
+
* <FormProvider {...methods}>
|
|
1531
|
+
* <form onSubmit={methods.handleSubmit(onSubmit)}>{children}</form>
|
|
1532
|
+
* </FormProvider>
|
|
1585
1533
|
* ```
|
|
1586
1534
|
*/
|
|
1587
1535
|
const FormProvider = ({ children, watch, getValues, getErrors, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, resetDefaultValues, handleSubmit, unregister, control, register, setFocus, subscribe, }) => {
|
|
@@ -1635,25 +1583,17 @@ function defaultValidateStatus(status) {
|
|
|
1635
1583
|
return status >= 200 && status < 300;
|
|
1636
1584
|
}
|
|
1637
1585
|
/**
|
|
1638
|
-
* Form component
|
|
1586
|
+
* Form component that handles submission, including optional `action` fetch and server error wiring.
|
|
1639
1587
|
*
|
|
1640
|
-
* @
|
|
1641
|
-
*
|
|
1642
|
-
* @returns form component or headless render prop.
|
|
1588
|
+
* @see [API](https://react-hook-form.com/docs/useform/form)
|
|
1643
1589
|
*
|
|
1644
1590
|
* @example
|
|
1645
1591
|
* ```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
|
-
* }
|
|
1592
|
+
* <Form action="/api" control={control}>
|
|
1593
|
+
* <input {...register("name")} />
|
|
1594
|
+
* <p>{errors?.root?.server && 'Server error'}</p>
|
|
1595
|
+
* <button>Submit</button>
|
|
1596
|
+
* </Form>
|
|
1657
1597
|
* ```
|
|
1658
1598
|
*/
|
|
1659
1599
|
function Form(props) {
|
|
@@ -2011,7 +1951,7 @@ var hasValidation = (options) => options.mount &&
|
|
|
2011
1951
|
|
|
2012
1952
|
function schemaErrorLookup(errors, _fields, name) {
|
|
2013
1953
|
const error = get(errors, name);
|
|
2014
|
-
if (error ||
|
|
1954
|
+
if ((error === null || error === void 0 ? void 0 : error.type) || (error === null || error === void 0 ? void 0 : error.message) || Array.isArray(error)) {
|
|
2015
1955
|
return {
|
|
2016
1956
|
error,
|
|
2017
1957
|
name,
|
|
@@ -2165,24 +2105,35 @@ function createFormControl(props = {}) {
|
|
|
2165
2105
|
let _proxySubscribeFormState = {
|
|
2166
2106
|
..._proxyFormState,
|
|
2167
2107
|
};
|
|
2108
|
+
const _isTracked = (...keys) => keys.some((key) => _proxyFormState[key] || _proxySubscribeFormState[key]);
|
|
2168
2109
|
const _subjects = {
|
|
2169
2110
|
array: createSubject(),
|
|
2170
2111
|
state: createSubject(),
|
|
2171
2112
|
};
|
|
2172
2113
|
let _setValidCallId = 0;
|
|
2173
|
-
|
|
2114
|
+
let _resetCallId = 0;
|
|
2115
|
+
let shouldDisplayAllAssociatedErrors = _options.criteriaMode === VALIDATION_MODE.all;
|
|
2174
2116
|
const debounce = (name, callback) => (wait) => {
|
|
2175
2117
|
clearTimeout(timers[name]);
|
|
2176
2118
|
timers[name] = setTimeout(callback, wait);
|
|
2177
2119
|
};
|
|
2120
|
+
const cancelDelayedError = (name) => {
|
|
2121
|
+
clearTimeout(timers[name]);
|
|
2122
|
+
delete timers[name];
|
|
2123
|
+
delete delayErrorCallbacks[name];
|
|
2124
|
+
};
|
|
2125
|
+
const cancelDelayedErrorTree = (name) => {
|
|
2126
|
+
cancelDelayedError(name);
|
|
2127
|
+
const prefix = `${name}.`;
|
|
2128
|
+
for (const key of Object.keys(delayErrorCallbacks)) {
|
|
2129
|
+
key.startsWith(prefix) && cancelDelayedError(key);
|
|
2130
|
+
}
|
|
2131
|
+
};
|
|
2178
2132
|
const _setValid = async (shouldUpdateValid) => {
|
|
2179
2133
|
if (_state.keepIsValid) {
|
|
2180
2134
|
return;
|
|
2181
2135
|
}
|
|
2182
|
-
if (!_options.disabled &&
|
|
2183
|
-
(_proxyFormState.isValid ||
|
|
2184
|
-
_proxySubscribeFormState.isValid ||
|
|
2185
|
-
shouldUpdateValid)) {
|
|
2136
|
+
if (!_options.disabled && (_isTracked('isValid') || shouldUpdateValid)) {
|
|
2186
2137
|
const callId = ++_setValidCallId;
|
|
2187
2138
|
let isValid;
|
|
2188
2139
|
if (_options.resolver) {
|
|
@@ -2204,11 +2155,7 @@ function createFormControl(props = {}) {
|
|
|
2204
2155
|
}
|
|
2205
2156
|
};
|
|
2206
2157
|
const _updateIsValidating = (names, isValidating) => {
|
|
2207
|
-
if (!_options.disabled &&
|
|
2208
|
-
(_proxyFormState.isValidating ||
|
|
2209
|
-
_proxyFormState.validatingFields ||
|
|
2210
|
-
_proxySubscribeFormState.isValidating ||
|
|
2211
|
-
_proxySubscribeFormState.validatingFields)) {
|
|
2158
|
+
if (!_options.disabled && _isTracked('isValidating', 'validatingFields')) {
|
|
2212
2159
|
(names || _names.mount).forEach((name) => {
|
|
2213
2160
|
if (name) {
|
|
2214
2161
|
isValidating
|
|
@@ -2247,14 +2194,13 @@ function createFormControl(props = {}) {
|
|
|
2247
2194
|
unsetEmptyArray(_formState.errors, name);
|
|
2248
2195
|
}
|
|
2249
2196
|
const touchedFieldsArray = get(_formState.touchedFields, name);
|
|
2250
|
-
if ((
|
|
2251
|
-
_proxySubscribeFormState.touchedFields) &&
|
|
2197
|
+
if (_isTracked('touchedFields') &&
|
|
2252
2198
|
shouldUpdateFieldsAndState &&
|
|
2253
2199
|
Array.isArray(touchedFieldsArray)) {
|
|
2254
2200
|
const touchedFields = method(touchedFieldsArray, args.argA, args.argB);
|
|
2255
2201
|
shouldSetValues && set(_formState.touchedFields, name, touchedFields);
|
|
2256
2202
|
}
|
|
2257
|
-
if (
|
|
2203
|
+
if (_isTracked('dirtyFields')) {
|
|
2258
2204
|
_updateDirtyFields();
|
|
2259
2205
|
}
|
|
2260
2206
|
_subjects.state.next({
|
|
@@ -2277,6 +2223,7 @@ function createFormControl(props = {}) {
|
|
|
2277
2223
|
});
|
|
2278
2224
|
};
|
|
2279
2225
|
const _setErrors = (errors) => {
|
|
2226
|
+
Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
|
|
2280
2227
|
_formState.errors = errors;
|
|
2281
2228
|
_subjects.state.next({
|
|
2282
2229
|
errors: _formState.errors,
|
|
@@ -2299,7 +2246,7 @@ function createFormControl(props = {}) {
|
|
|
2299
2246
|
}
|
|
2300
2247
|
return false;
|
|
2301
2248
|
};
|
|
2302
|
-
const
|
|
2249
|
+
const isStaleArrayField = (name) => {
|
|
2303
2250
|
if (!_state.actionArrayLengths.size) {
|
|
2304
2251
|
return false;
|
|
2305
2252
|
}
|
|
@@ -2326,13 +2273,19 @@ function createFormControl(props = {}) {
|
|
|
2326
2273
|
ownerPreActionLength = _state.actionArrayLengths.get(path);
|
|
2327
2274
|
}
|
|
2328
2275
|
node = node[key];
|
|
2276
|
+
if (isUndefined(node) &&
|
|
2277
|
+
ownerDepth !== -1 &&
|
|
2278
|
+
i > ownerDepth &&
|
|
2279
|
+
+segments[ownerDepth] < ownerPreActionLength) {
|
|
2280
|
+
return true;
|
|
2281
|
+
}
|
|
2329
2282
|
}
|
|
2330
2283
|
return false;
|
|
2331
2284
|
};
|
|
2332
2285
|
const updateValidAndValue = (name, shouldSkipSetValueAs, value, ref) => {
|
|
2333
2286
|
const field = get(_fields, name);
|
|
2334
2287
|
if (field) {
|
|
2335
|
-
if (hasExplicitNullIntermediate(name) ||
|
|
2288
|
+
if (hasExplicitNullIntermediate(name) || isStaleArrayField(name)) {
|
|
2336
2289
|
return;
|
|
2337
2290
|
}
|
|
2338
2291
|
const wasUnsetInFormValues = isUndefined(get(_formValues, name));
|
|
@@ -2346,7 +2299,7 @@ function createFormControl(props = {}) {
|
|
|
2346
2299
|
_setValid();
|
|
2347
2300
|
if (wasUnsetInFormValues &&
|
|
2348
2301
|
_formState.isDirty &&
|
|
2349
|
-
(
|
|
2302
|
+
_isTracked('isDirty')) {
|
|
2350
2303
|
const isDirty = _getDirty();
|
|
2351
2304
|
if (!isDirty) {
|
|
2352
2305
|
_formState.isDirty = false;
|
|
@@ -2373,7 +2326,7 @@ function createFormControl(props = {}) {
|
|
|
2373
2326
|
if (!_options.disabled || shouldDirty === true) {
|
|
2374
2327
|
if (!isBlurEvent || shouldDirty) {
|
|
2375
2328
|
const isCurrentFieldPristine = deepEqual(get(_defaultValues, name), fieldValue);
|
|
2376
|
-
if (
|
|
2329
|
+
if (_isTracked('isDirty')) {
|
|
2377
2330
|
isPreviousDirty = _formState.isDirty;
|
|
2378
2331
|
_formState.isDirty = output.isDirty =
|
|
2379
2332
|
!isCurrentFieldPristine || _getDirty();
|
|
@@ -2391,8 +2344,7 @@ function createFormControl(props = {}) {
|
|
|
2391
2344
|
output.dirtyFields = _formState.dirtyFields;
|
|
2392
2345
|
shouldUpdateField =
|
|
2393
2346
|
shouldUpdateField ||
|
|
2394
|
-
((
|
|
2395
|
-
_proxySubscribeFormState.dirtyFields) &&
|
|
2347
|
+
(_isTracked('dirtyFields') &&
|
|
2396
2348
|
isPreviousDirty !== !isCurrentFieldPristine);
|
|
2397
2349
|
}
|
|
2398
2350
|
if (isBlurEvent) {
|
|
@@ -2402,8 +2354,7 @@ function createFormControl(props = {}) {
|
|
|
2402
2354
|
output.touchedFields = _formState.touchedFields;
|
|
2403
2355
|
shouldUpdateField =
|
|
2404
2356
|
shouldUpdateField ||
|
|
2405
|
-
((
|
|
2406
|
-
_proxySubscribeFormState.touchedFields) &&
|
|
2357
|
+
(_isTracked('touchedFields') &&
|
|
2407
2358
|
isPreviousFieldTouched !== isBlurEvent);
|
|
2408
2359
|
}
|
|
2409
2360
|
}
|
|
@@ -2413,7 +2364,7 @@ function createFormControl(props = {}) {
|
|
|
2413
2364
|
};
|
|
2414
2365
|
const shouldRenderByError = (name, isValid, error, fieldState) => {
|
|
2415
2366
|
const previousFieldError = get(_formState.errors, name);
|
|
2416
|
-
const shouldUpdateValid = (
|
|
2367
|
+
const shouldUpdateValid = _isTracked('isValid') &&
|
|
2417
2368
|
isBoolean(isValid) &&
|
|
2418
2369
|
_formState.isValid !== isValid;
|
|
2419
2370
|
if (_options.delayError && error) {
|
|
@@ -2421,8 +2372,7 @@ function createFormControl(props = {}) {
|
|
|
2421
2372
|
delayErrorCallbacks[name](_options.delayError);
|
|
2422
2373
|
}
|
|
2423
2374
|
else {
|
|
2424
|
-
|
|
2425
|
-
delete delayErrorCallbacks[name];
|
|
2375
|
+
cancelDelayedError(name);
|
|
2426
2376
|
error
|
|
2427
2377
|
? set(_formState.errors, name, error)
|
|
2428
2378
|
: unset(_formState.errors, name);
|
|
@@ -2445,22 +2395,34 @@ function createFormControl(props = {}) {
|
|
|
2445
2395
|
return await _options.resolver(_formValues, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation));
|
|
2446
2396
|
};
|
|
2447
2397
|
const executeSchemaAndUpdateState = async (names) => {
|
|
2398
|
+
const resetCallId = _resetCallId;
|
|
2448
2399
|
const { errors } = await _runSchema(names);
|
|
2400
|
+
if (resetCallId !== _resetCallId) {
|
|
2401
|
+
return errors;
|
|
2402
|
+
}
|
|
2449
2403
|
_updateIsValidating(names);
|
|
2450
2404
|
if (names) {
|
|
2451
2405
|
for (const name of names) {
|
|
2452
2406
|
const error = get(errors, name);
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2407
|
+
cancelDelayedError(name);
|
|
2408
|
+
const isFieldArrayRootError = _names.array.has(name) &&
|
|
2409
|
+
isObject(error) &&
|
|
2410
|
+
!Object.keys(error).some((key) => !Number.isNaN(Number(key)));
|
|
2411
|
+
const field = get(_fields, name);
|
|
2412
|
+
const hasNestedFields = isObject(field) && Object.keys(field).some((key) => key !== '_f');
|
|
2413
|
+
isFieldArrayRootError
|
|
2414
|
+
? updateFieldArrayRootError(_formState.errors, { [name]: error }, name)
|
|
2415
|
+
: (error === null || error === void 0 ? void 0 : error.type) ||
|
|
2416
|
+
(error === null || error === void 0 ? void 0 : error.message) ||
|
|
2417
|
+
Array.isArray(error) ||
|
|
2418
|
+
(isObject(error) && hasNestedFields)
|
|
2419
|
+
? set(_formState.errors, name, error)
|
|
2420
|
+
: unset(_formState.errors, name);
|
|
2460
2421
|
}
|
|
2461
2422
|
_formState.errors = { ..._formState.errors };
|
|
2462
2423
|
}
|
|
2463
2424
|
else {
|
|
2425
|
+
Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
|
|
2464
2426
|
_formState.errors = errors;
|
|
2465
2427
|
}
|
|
2466
2428
|
return errors;
|
|
@@ -2521,10 +2483,7 @@ function createFormControl(props = {}) {
|
|
|
2521
2483
|
if (_f) {
|
|
2522
2484
|
const isFieldArrayRoot = _names.array.has(_f.name);
|
|
2523
2485
|
const isPromiseFunction = field._f && hasPromiseValidation(field._f);
|
|
2524
|
-
const shouldTrackIsValidatingState =
|
|
2525
|
-
_proxyFormState.isValidating ||
|
|
2526
|
-
_proxySubscribeFormState.validatingFields ||
|
|
2527
|
-
_proxySubscribeFormState.isValidating;
|
|
2486
|
+
const shouldTrackIsValidatingState = _isTracked('isValidating', 'validatingFields');
|
|
2528
2487
|
if (isPromiseFunction && shouldTrackIsValidatingState) {
|
|
2529
2488
|
_updateIsValidating([_f.name], true);
|
|
2530
2489
|
}
|
|
@@ -2538,12 +2497,14 @@ function createFormControl(props = {}) {
|
|
|
2538
2497
|
break;
|
|
2539
2498
|
}
|
|
2540
2499
|
}
|
|
2541
|
-
!onlyCheckValid
|
|
2542
|
-
(
|
|
2500
|
+
if (!onlyCheckValid) {
|
|
2501
|
+
cancelDelayedError(_f.name);
|
|
2502
|
+
get(fieldError, _f.name)
|
|
2543
2503
|
? isFieldArrayRoot
|
|
2544
2504
|
? updateFieldArrayRootError(_formState.errors, fieldError, _f.name)
|
|
2545
2505
|
: set(_formState.errors, _f.name, fieldError[_f.name])
|
|
2546
|
-
: unset(_formState.errors, _f.name)
|
|
2506
|
+
: unset(_formState.errors, _f.name);
|
|
2507
|
+
}
|
|
2547
2508
|
if (props.shouldUseNativeValidation && fieldError[_f.name]) {
|
|
2548
2509
|
break;
|
|
2549
2510
|
}
|
|
@@ -2629,7 +2590,14 @@ function createFormControl(props = {}) {
|
|
|
2629
2590
|
}
|
|
2630
2591
|
}
|
|
2631
2592
|
(options.shouldDirty || options.shouldTouch) &&
|
|
2632
|
-
updateTouchAndDirty(name,
|
|
2593
|
+
updateTouchAndDirty(name, field &&
|
|
2594
|
+
field._f &&
|
|
2595
|
+
!field._f.disabled &&
|
|
2596
|
+
(field._f.valueAsNumber ||
|
|
2597
|
+
field._f.valueAsDate ||
|
|
2598
|
+
field._f.setValueAs)
|
|
2599
|
+
? getFieldValueAs(value, field._f)
|
|
2600
|
+
: fieldValue, options.shouldTouch, options.shouldDirty, !skipRender);
|
|
2633
2601
|
options.shouldValidate &&
|
|
2634
2602
|
trigger(name, {
|
|
2635
2603
|
delayError: options.delayError,
|
|
@@ -2671,11 +2639,7 @@ function createFormControl(props = {}) {
|
|
|
2671
2639
|
name,
|
|
2672
2640
|
values: skipClone ? _formValues : cloneObject(_formValues),
|
|
2673
2641
|
});
|
|
2674
|
-
if ((
|
|
2675
|
-
_proxyFormState.dirtyFields ||
|
|
2676
|
-
_proxySubscribeFormState.isDirty ||
|
|
2677
|
-
_proxySubscribeFormState.dirtyFields) &&
|
|
2678
|
-
options.shouldDirty) {
|
|
2642
|
+
if (_isTracked('isDirty', 'dirtyFields') && options.shouldDirty) {
|
|
2679
2643
|
_updateDirtyFields();
|
|
2680
2644
|
if (!skipStateEmit) {
|
|
2681
2645
|
_subjects.state.next({
|
|
@@ -2788,7 +2752,7 @@ function createFormControl(props = {}) {
|
|
|
2788
2752
|
});
|
|
2789
2753
|
if (shouldSkipValidation) {
|
|
2790
2754
|
if ((!hasNoValidationEffect || !_formState.isValid) &&
|
|
2791
|
-
(
|
|
2755
|
+
_isTracked('isValid')) {
|
|
2792
2756
|
if (_options.mode === 'onBlur') {
|
|
2793
2757
|
if (isBlurEvent) {
|
|
2794
2758
|
_setValid();
|
|
@@ -2831,8 +2795,7 @@ function createFormControl(props = {}) {
|
|
|
2831
2795
|
if (error) {
|
|
2832
2796
|
isValid = false;
|
|
2833
2797
|
}
|
|
2834
|
-
else if (
|
|
2835
|
-
_proxySubscribeFormState.isValid) {
|
|
2798
|
+
else if (_isTracked('isValid')) {
|
|
2836
2799
|
isValid = await executeBuiltInValidation({
|
|
2837
2800
|
fields: _fields,
|
|
2838
2801
|
onlyCheckValid: true,
|
|
@@ -2862,11 +2825,15 @@ function createFormControl(props = {}) {
|
|
|
2862
2825
|
let validationResult;
|
|
2863
2826
|
const fieldNames = convertToArrayPayload(name);
|
|
2864
2827
|
if (_options.resolver) {
|
|
2828
|
+
const resetCallId = _resetCallId;
|
|
2865
2829
|
const errors = await executeSchemaAndUpdateState(isUndefined(name) ? name : fieldNames);
|
|
2866
2830
|
isValid = isEmptyObject(errors);
|
|
2867
2831
|
validationResult = name
|
|
2868
2832
|
? !fieldNames.some((name) => get(errors, name))
|
|
2869
2833
|
: isValid;
|
|
2834
|
+
if (resetCallId !== _resetCallId) {
|
|
2835
|
+
return validationResult;
|
|
2836
|
+
}
|
|
2870
2837
|
}
|
|
2871
2838
|
else if (name) {
|
|
2872
2839
|
validationResult = (await Promise.all(fieldNames.map(async (fieldName) => {
|
|
@@ -2893,8 +2860,7 @@ function createFormControl(props = {}) {
|
|
|
2893
2860
|
delayErrorCallbacks[name](_options.delayError);
|
|
2894
2861
|
}
|
|
2895
2862
|
else {
|
|
2896
|
-
|
|
2897
|
-
delete delayErrorCallbacks[name];
|
|
2863
|
+
cancelDelayedError(name);
|
|
2898
2864
|
}
|
|
2899
2865
|
}
|
|
2900
2866
|
if (options.shouldTouch) {
|
|
@@ -2905,13 +2871,11 @@ function createFormControl(props = {}) {
|
|
|
2905
2871
|
}
|
|
2906
2872
|
_subjects.state.next({
|
|
2907
2873
|
...(!isString(name) ||
|
|
2908
|
-
((
|
|
2909
|
-
isValid !== _formState.isValid)
|
|
2874
|
+
(_isTracked('isValid') && isValid !== _formState.isValid)
|
|
2910
2875
|
? {}
|
|
2911
2876
|
: { name }),
|
|
2912
2877
|
...(_options.resolver || !name ? { isValid } : {}),
|
|
2913
|
-
...(options.shouldTouch &&
|
|
2914
|
-
(_proxyFormState.touchedFields || _proxySubscribeFormState.touchedFields)
|
|
2878
|
+
...(options.shouldTouch && _isTracked('touchedFields')
|
|
2915
2879
|
? { touchedFields: _formState.touchedFields }
|
|
2916
2880
|
: {}),
|
|
2917
2881
|
errors: _formState.errors,
|
|
@@ -2946,15 +2910,16 @@ function createFormControl(props = {}) {
|
|
|
2946
2910
|
invalid: !!error,
|
|
2947
2911
|
isDirty: !!get(targetFormState.dirtyFields, name),
|
|
2948
2912
|
error,
|
|
2949
|
-
isValidating: !!get(
|
|
2913
|
+
isValidating: !!get(targetFormState.validatingFields, name),
|
|
2950
2914
|
isTouched: !!get(targetFormState.touchedFields, name),
|
|
2951
2915
|
};
|
|
2952
2916
|
};
|
|
2953
2917
|
const clearErrors = (name) => {
|
|
2954
2918
|
const names = name ? convertToArrayPayload(name) : undefined;
|
|
2955
|
-
names === null || names === void 0 ? void 0 : names.forEach((inputName) => unset(_formState.errors, inputName));
|
|
2956
2919
|
if (names) {
|
|
2957
2920
|
names.forEach((inputName) => {
|
|
2921
|
+
cancelDelayedErrorTree(inputName);
|
|
2922
|
+
unset(_formState.errors, inputName);
|
|
2958
2923
|
_subjects.state.next({
|
|
2959
2924
|
name: inputName,
|
|
2960
2925
|
errors: _formState.errors,
|
|
@@ -2962,6 +2927,7 @@ function createFormControl(props = {}) {
|
|
|
2962
2927
|
});
|
|
2963
2928
|
}
|
|
2964
2929
|
else {
|
|
2930
|
+
Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
|
|
2965
2931
|
_formState.errors = {};
|
|
2966
2932
|
_subjects.state.next({
|
|
2967
2933
|
errors: _formState.errors,
|
|
@@ -2969,9 +2935,10 @@ function createFormControl(props = {}) {
|
|
|
2969
2935
|
}
|
|
2970
2936
|
};
|
|
2971
2937
|
const setError = (name, error, options) => {
|
|
2938
|
+
cancelDelayedError(name);
|
|
2972
2939
|
const ref = (get(_fields, name, { _f: {} })._f || {}).ref;
|
|
2973
2940
|
const currentError = get(_formState.errors, name) || {};
|
|
2974
|
-
const { ref: currentRef, message, type, ...restOfErrorTree } = currentError;
|
|
2941
|
+
const { ref: currentRef, message, type, types, ...restOfErrorTree } = currentError;
|
|
2975
2942
|
set(_formState.errors, name, {
|
|
2976
2943
|
...restOfErrorTree,
|
|
2977
2944
|
...error,
|
|
@@ -3056,11 +3023,15 @@ function createFormControl(props = {}) {
|
|
|
3056
3023
|
for (const fieldName of name ? convertToArrayPayload(name) : _names.mount) {
|
|
3057
3024
|
_names.mount.delete(fieldName);
|
|
3058
3025
|
_names.array.delete(fieldName);
|
|
3026
|
+
_names.disabled.delete(fieldName);
|
|
3059
3027
|
if (!options.keepValue) {
|
|
3060
3028
|
unset(_fields, fieldName);
|
|
3061
3029
|
unset(_formValues, fieldName);
|
|
3062
3030
|
}
|
|
3063
|
-
!options.keepError
|
|
3031
|
+
if (!options.keepError) {
|
|
3032
|
+
cancelDelayedErrorTree(fieldName);
|
|
3033
|
+
unset(_formState.errors, fieldName);
|
|
3034
|
+
}
|
|
3064
3035
|
!options.keepDirty && unset(_formState.dirtyFields, fieldName);
|
|
3065
3036
|
!options.keepTouched && unset(_formState.touchedFields, fieldName);
|
|
3066
3037
|
!options.keepIsValidating &&
|
|
@@ -3076,6 +3047,9 @@ function createFormControl(props = {}) {
|
|
|
3076
3047
|
_subjects.state.next({
|
|
3077
3048
|
..._formState,
|
|
3078
3049
|
...(options.keepDirty ? {} : { isDirty: _getDirty() }),
|
|
3050
|
+
...(options.keepIsValidating
|
|
3051
|
+
? {}
|
|
3052
|
+
: { isValidating: !isEmptyObject(_formState.validatingFields) }),
|
|
3079
3053
|
});
|
|
3080
3054
|
!options.keepIsValid && _setValid();
|
|
3081
3055
|
};
|
|
@@ -3198,7 +3172,7 @@ function createFormControl(props = {}) {
|
|
|
3198
3172
|
});
|
|
3199
3173
|
}
|
|
3200
3174
|
}
|
|
3201
|
-
}, 0
|
|
3175
|
+
}, 0);
|
|
3202
3176
|
}
|
|
3203
3177
|
};
|
|
3204
3178
|
const handleSubmit = (onValid, onInvalid) => async (e) => {
|
|
@@ -3214,8 +3188,13 @@ function createFormControl(props = {}) {
|
|
|
3214
3188
|
isSubmitting: true,
|
|
3215
3189
|
});
|
|
3216
3190
|
if (_options.resolver) {
|
|
3191
|
+
const resetCallId = _resetCallId;
|
|
3217
3192
|
const { errors, values } = await _runSchema();
|
|
3193
|
+
if (resetCallId !== _resetCallId) {
|
|
3194
|
+
return;
|
|
3195
|
+
}
|
|
3218
3196
|
_updateIsValidating();
|
|
3197
|
+
Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
|
|
3219
3198
|
_formState.errors = errors;
|
|
3220
3199
|
fieldValues = cloneObject(values);
|
|
3221
3200
|
}
|
|
@@ -3224,13 +3203,13 @@ function createFormControl(props = {}) {
|
|
|
3224
3203
|
fields: _fields,
|
|
3225
3204
|
eventType: EVENTS.SUBMIT,
|
|
3226
3205
|
});
|
|
3206
|
+
unset(_formState.errors, ROOT_ERROR_TYPE);
|
|
3227
3207
|
}
|
|
3228
3208
|
if (_names.disabled.size) {
|
|
3229
3209
|
for (const name of _names.disabled) {
|
|
3230
3210
|
unset(fieldValues, name);
|
|
3231
3211
|
}
|
|
3232
3212
|
}
|
|
3233
|
-
unset(_formState.errors, ROOT_ERROR_TYPE);
|
|
3234
3213
|
if (isEmptyObject(_formState.errors)) {
|
|
3235
3214
|
_subjects.state.next({
|
|
3236
3215
|
errors: {},
|
|
@@ -3263,6 +3242,7 @@ function createFormControl(props = {}) {
|
|
|
3263
3242
|
};
|
|
3264
3243
|
const resetField = (name, options = {}) => {
|
|
3265
3244
|
if (get(_fields, name)) {
|
|
3245
|
+
unset(_formState.validatingFields, name);
|
|
3266
3246
|
if (isUndefined(options.defaultValue)) {
|
|
3267
3247
|
setValue(name, cloneObject(get(_defaultValues, name)));
|
|
3268
3248
|
}
|
|
@@ -3280,18 +3260,24 @@ function createFormControl(props = {}) {
|
|
|
3280
3260
|
: _getDirty();
|
|
3281
3261
|
}
|
|
3282
3262
|
if (!options.keepError) {
|
|
3263
|
+
cancelDelayedError(name);
|
|
3283
3264
|
unset(_formState.errors, name);
|
|
3284
3265
|
_setValid();
|
|
3285
3266
|
}
|
|
3286
|
-
_subjects.state.next({
|
|
3267
|
+
_subjects.state.next({
|
|
3268
|
+
..._formState,
|
|
3269
|
+
isValidating: !isEmptyObject(_formState.validatingFields),
|
|
3270
|
+
});
|
|
3287
3271
|
}
|
|
3288
3272
|
};
|
|
3289
3273
|
const _reset = (formValues, keepStateOptions = {}) => {
|
|
3274
|
+
_resetCallId++;
|
|
3290
3275
|
const updatedValues = formValues ? cloneObject(formValues) : _defaultValues;
|
|
3291
3276
|
const cloneUpdatedValues = cloneObject(updatedValues);
|
|
3292
3277
|
const isEmptyResetValues = isEmptyObject(formValues);
|
|
3293
3278
|
const values = cloneUpdatedValues;
|
|
3294
3279
|
const fieldRefs = _fields;
|
|
3280
|
+
Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
|
|
3295
3281
|
if (!keepStateOptions.keepDefaultValues) {
|
|
3296
3282
|
_defaultValues = updatedValues;
|
|
3297
3283
|
}
|
|
@@ -3409,10 +3395,16 @@ function createFormControl(props = {}) {
|
|
|
3409
3395
|
? getDirtyFields(_defaultValues, formValues, undefined, fieldRefs)
|
|
3410
3396
|
: keepStateOptions.keepDirty
|
|
3411
3397
|
? _formState.dirtyFields
|
|
3412
|
-
:
|
|
3398
|
+
: keepStateOptions.keepValues
|
|
3399
|
+
? getDirtyFields(_defaultValues, _formValues, undefined, fieldRefs)
|
|
3400
|
+
: {},
|
|
3413
3401
|
touchedFields: keepStateOptions.keepTouched
|
|
3414
3402
|
? _formState.touchedFields
|
|
3415
3403
|
: {},
|
|
3404
|
+
...(!keepStateOptions.keepIsValidating &&
|
|
3405
|
+
(_formState.isValidating || !isEmptyObject(_formState.validatingFields))
|
|
3406
|
+
? { validatingFields: {}, isValidating: false }
|
|
3407
|
+
: null),
|
|
3416
3408
|
errors: keepStateOptions.keepErrors ? _formState.errors : {},
|
|
3417
3409
|
isSubmitSuccessful: keepStateOptions.keepIsSubmitSuccessful
|
|
3418
3410
|
? _formState.isSubmitSuccessful
|
|
@@ -3534,6 +3526,8 @@ function createFormControl(props = {}) {
|
|
|
3534
3526
|
};
|
|
3535
3527
|
_validationModeBeforeSubmit = getValidationModes(_options.mode);
|
|
3536
3528
|
_validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
|
|
3529
|
+
shouldDisplayAllAssociatedErrors =
|
|
3530
|
+
_options.criteriaMode === VALIDATION_MODE.all;
|
|
3537
3531
|
},
|
|
3538
3532
|
},
|
|
3539
3533
|
subscribe,
|
|
@@ -3561,32 +3555,17 @@ function createFormControl(props = {}) {
|
|
|
3561
3555
|
}
|
|
3562
3556
|
|
|
3563
3557
|
/**
|
|
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)
|
|
3558
|
+
* Core hook for managing a form. Returns all methods and state for
|
|
3559
|
+
* registration, validation, and submission.
|
|
3568
3560
|
*
|
|
3569
|
-
* @
|
|
3570
|
-
*
|
|
3571
|
-
* @returns methods - individual functions to manage the form state. {@link UseFormReturn}
|
|
3561
|
+
* @see [API](https://react-hook-form.com/docs/useform)
|
|
3572
3562
|
*
|
|
3573
3563
|
* @example
|
|
3574
3564
|
* ```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
|
-
* }
|
|
3565
|
+
* const { register, handleSubmit, formState: { errors } } = useForm<FormValues>();
|
|
3566
|
+
* <form onSubmit={handleSubmit(onSubmit)}>
|
|
3567
|
+
* <input {...register("email", { required: true })} />
|
|
3568
|
+
* </form>
|
|
3590
3569
|
* ```
|
|
3591
3570
|
*/
|
|
3592
3571
|
function useForm(props = {}) {
|
|
@@ -3624,12 +3603,12 @@ function useForm(props = {}) {
|
|
|
3624
3603
|
}
|
|
3625
3604
|
const control = _formControl.current.control;
|
|
3626
3605
|
control._options = props;
|
|
3627
|
-
const
|
|
3606
|
+
const getCurrentFormState = () => ({
|
|
3607
|
+
...control._formState,
|
|
3608
|
+
defaultValues: control._defaultValues,
|
|
3609
|
+
});
|
|
3610
|
+
const { resyncIfNeeded, snapshot } = useResyncOnReconnect(getCurrentFormState);
|
|
3628
3611
|
useIsomorphicLayoutEffect(() => {
|
|
3629
|
-
const getCurrentFormState = () => ({
|
|
3630
|
-
...control._formState,
|
|
3631
|
-
defaultValues: control._defaultValues,
|
|
3632
|
-
});
|
|
3633
3612
|
resyncIfNeeded(true, getCurrentFormState, updateFormState);
|
|
3634
3613
|
const unsubscribe = control._subscribe({
|
|
3635
3614
|
formState: control._proxyFormState,
|
|
@@ -3713,32 +3692,20 @@ function useForm(props = {}) {
|
|
|
3713
3692
|
}
|
|
3714
3693
|
|
|
3715
3694
|
/**
|
|
3716
|
-
*
|
|
3695
|
+
* Component wrapper around `useWatch`. Re-renders only when watched fields change.
|
|
3717
3696
|
*
|
|
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
|
|
3697
|
+
* @see [API](https://react-hook-form.com/docs/usewatch)
|
|
3726
3698
|
*
|
|
3727
3699
|
* @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
3700
|
* ```tsx
|
|
3732
|
-
* const { control } = useForm();
|
|
3733
|
-
*
|
|
3734
3701
|
* <Watch
|
|
3735
3702
|
* control={control}
|
|
3736
|
-
* names={[
|
|
3737
|
-
* render={([foo, bar
|
|
3703
|
+
* names={["foo", "bar"]}
|
|
3704
|
+
* render={([foo, bar]) => <span>{foo} {bar}</span>}
|
|
3738
3705
|
* />
|
|
3739
3706
|
* ```
|
|
3740
3707
|
*/
|
|
3741
3708
|
const Watch = (props) => props.render(useWatch({ name: props.names, ...props }));
|
|
3742
3709
|
|
|
3743
|
-
export { Controller, FieldArray, Form, FormProvider, FormState, FormStateSubscribe, Watch, appendErrors, createFormControl, get, set, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch };
|
|
3710
|
+
export { Controller, ErrorMessage, FieldArray, Form, FormProvider, FormState, FormStateSubscribe, Watch, appendErrors, createFormControl, get, set, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch };
|
|
3744
3711
|
//# sourceMappingURL=index.esm.mjs.map
|