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.
Files changed (41) hide show
  1. package/dist/controller.d.ts +7 -35
  2. package/dist/controller.d.ts.map +1 -1
  3. package/dist/errorMessage.d.ts +27 -0
  4. package/dist/errorMessage.d.ts.map +1 -0
  5. package/dist/form.d.ts +7 -15
  6. package/dist/form.d.ts.map +1 -1
  7. package/dist/index.cjs.js +1 -1
  8. package/dist/index.cjs.js.map +1 -1
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.esm.mjs +262 -295
  12. package/dist/index.esm.mjs.map +1 -1
  13. package/dist/index.react-server.d.ts +3 -0
  14. package/dist/index.react-server.d.ts.map +1 -0
  15. package/dist/index.umd.js +1 -1
  16. package/dist/index.umd.js.map +1 -1
  17. package/dist/logic/createFormControl.d.ts.map +1 -1
  18. package/dist/logic/iterateFieldsByAction.d.ts +1 -1
  19. package/dist/logic/iterateFieldsByAction.d.ts.map +1 -1
  20. package/dist/logic/schemaErrorLookup.d.ts.map +1 -1
  21. package/dist/logic/validateField.d.ts.map +1 -1
  22. package/dist/react-server.esm.mjs +2464 -0
  23. package/dist/react-server.esm.mjs.map +1 -0
  24. package/dist/useController.d.ts +5 -17
  25. package/dist/useController.d.ts.map +1 -1
  26. package/dist/useFieldArray.d.ts +5 -30
  27. package/dist/useFieldArray.d.ts.map +1 -1
  28. package/dist/useForm.d.ts +7 -22
  29. package/dist/useForm.d.ts.map +1 -1
  30. package/dist/useFormContext.d.ts +10 -46
  31. package/dist/useFormContext.d.ts.map +1 -1
  32. package/dist/useFormState.d.ts +4 -23
  33. package/dist/useFormState.d.ts.map +1 -1
  34. package/dist/useWatch.d.ts +7 -140
  35. package/dist/useWatch.d.ts.map +1 -1
  36. package/dist/utils/cloneObject.d.ts.map +1 -1
  37. package/dist/utils/flatten.d.ts.map +1 -1
  38. package/dist/utils/formData.d.ts.map +1 -1
  39. package/dist/watch.d.ts +4 -16
  40. package/dist/watch.d.ts.map +1 -1
  41. package/package.json +2 -1
@@ -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 && (data instanceof Blob || isFileListInstance)) {
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 each form state and isolates re-renders at the custom hook level. It has its own scope for form state subscriptions, so it will not affect other useFormState or useForm instances. Using this hook can reduce the re-render impact on large and complex form applications.
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
- * @remarks
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
- * function App() {
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
- * Custom hook to subscribe to field changes and isolate re-rendering at the component level.
349
+ * Subscribes to field value changes and isolates re-renders to the hook level.
368
350
  *
369
- * @remarks
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 } = useForm();
376
- * const values = useWatch({
377
- * name: "fieldName",
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
- * Custom hook to work with controlled component, this function provide you with both form and field level state. Re-render is isolated at the hook level.
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
- * @param props - the path name to the form field value, and validation rules.
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
- * function Input(props) {
481
- * const { field, fieldState, formState } = useController(props);
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 based on `useController` hook to work with controlled component.
609
+ * Component wrapper around `useController` for controlled inputs.
644
610
  *
645
- * @remarks
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
- * @param props - the path name to the form field value, and validation rules.
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
- * @returns provide field handler functions, field and form state.
628
+ * @see [API](https://react-hook-form.com/docs/useformstate/errormessage)
651
629
  *
652
630
  * @example
653
631
  * ```tsx
654
- * function App() {
655
- * const { control } = useForm<FormValues>({
656
- * defaultValues: {
657
- * test: ""
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 Controller = (props) => props.render(useController(props));
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, abortEarly) => {
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], key) && !abortEarly) {
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) && !abortEarly) {
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
- break;
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
- break;
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
- setCustomValidity(validateError.message);
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
- * A custom hook that exposes convenient methods to perform operations with a list of dynamic inputs that need to be appended, updated, removed etc. • [Demo](https://codesandbox.io/s/react-hook-form-usefieldarray-ssugn) • [Video](https://youtu.be/4MrbfGSFY2A)
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
- * @returns methods - functions to manipulate with the Field Arrays (dynamic inputs) {@link UseFieldArrayReturn}
1108
+ * @see [API](https://react-hook-form.com/docs/usefieldarray)
1143
1109
  *
1144
1110
  * @example
1145
1111
  * ```tsx
1146
- * function App() {
1147
- * const { register, control, handleSubmit, reset, trigger, setError } = useForm({
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 [fields, setFields] = React.useState(control._getFieldArray(name));
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
- return control._subjects.array.subscribe({
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
- }, [control, name, disabled]);
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, {}, true, false);
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
- result.append(key, flattenFormValues[key]);
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
- * This custom hook allows you to access the form context. useFormContext is intended to be used in deeply nested structures, where it would become inconvenient to pass the context as a prop. To be used with {@link FormProvider}.
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
- * @returns return all useForm methods
1514
+ * @see [API](https://react-hook-form.com/docs/useformcontext)
1533
1515
  *
1534
1516
  * @example
1535
1517
  * ```tsx
1536
- * function App() {
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
- * A provider component that propagates the `useForm` methods to all children components via [React Context](https://react.dev/reference/react/useContext) API. To be used with {@link useFormContext}.
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
- * @param props - all useForm methods
1526
+ * @see [API](https://react-hook-form.com/docs/useformcontext)
1564
1527
  *
1565
1528
  * @example
1566
1529
  * ```tsx
1567
- * function App() {
1568
- * const methods = useForm();
1569
- * const onSubmit = data => console.log(data);
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 to manage submission.
1586
+ * Form component that handles submission, including optional `action` fetch and server error wiring.
1639
1587
  *
1640
- * @param props - to setup submission detail. {@link FormProps}
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
- * function App() {
1647
- * const { control, formState: { errors } } = useForm();
1648
- *
1649
- * return (
1650
- * <Form action="/api" control={control}>
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 || isKey(name)) {
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
- const shouldDisplayAllAssociatedErrors = _options.criteriaMode === VALIDATION_MODE.all;
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 ((_proxyFormState.touchedFields ||
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 (_proxyFormState.dirtyFields || _proxySubscribeFormState.dirtyFields) {
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 isStaleArrayIndex = (name) => {
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) || isStaleArrayIndex(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
- (_proxyFormState.isDirty || _proxySubscribeFormState.isDirty)) {
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 (_proxyFormState.isDirty || _proxySubscribeFormState.isDirty) {
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
- ((_proxyFormState.dirtyFields ||
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
- ((_proxyFormState.touchedFields ||
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 = (_proxyFormState.isValid || _proxySubscribeFormState.isValid) &&
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
- clearTimeout(timers[name]);
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
- error
2454
- ? _names.array.has(name) &&
2455
- isObject(error) &&
2456
- !Object.keys(error).some((key) => !Number.isNaN(Number(key)))
2457
- ? updateFieldArrayRootError(_formState.errors, { [name]: error }, name)
2458
- : set(_formState.errors, name, error)
2459
- : unset(_formState.errors, name);
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 = _proxyFormState.validatingFields ||
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
- (get(fieldError, _f.name)
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, fieldValue, options.shouldTouch, options.shouldDirty, !skipRender);
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 ((_proxyFormState.isDirty ||
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
- (_proxyFormState.isValid || _proxySubscribeFormState.isValid)) {
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 (_proxyFormState.isValid ||
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
- clearTimeout(timers[name]);
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
- ((_proxyFormState.isValid || _proxySubscribeFormState.isValid) &&
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(_formState.validatingFields, name),
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 && unset(_formState.errors, fieldName);
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, false);
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({ ..._formState });
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
- * Custom hook to manage the entire form.
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
- * @param props - form configuration and validation parameters.
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
- * function App() {
3576
- * const { register, handleSubmit, watch, formState: { errors } } = useForm();
3577
- * const onSubmit = data => console.log(data);
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 { resyncIfNeeded, snapshot } = useResyncOnReconnect();
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
- * Watch component that subscribes to form field changes and re-renders when watched fields update.
3695
+ * Component wrapper around `useWatch`. Re-renders only when watched fields change.
3717
3696
  *
3718
- * @param control - The form control object from useForm
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={['foo', 'bar', 'baz.qux']}
3737
- * render={([foo, bar, baz_qux]) => <div>{foo}{bar}{baz_qux}</div>}
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