react-hook-form 7.25.1 → 7.26.1

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.
@@ -40,6 +40,7 @@ var get = (obj, path, defaultValue) => {
40
40
 
41
41
  const EVENTS = {
42
42
  BLUR: 'blur',
43
+ FOCUS_OUT: 'focusout',
43
44
  CHANGE: 'change',
44
45
  };
45
46
  const VALIDATION_MODE = {
@@ -66,7 +67,67 @@ var omit = (source, key) => {
66
67
  };
67
68
 
68
69
  const HookFormContext = React.createContext(null);
70
+ /**
71
+ * 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}.
72
+ *
73
+ * @remarks
74
+ * [API](https://react-hook-form.com/api/useformcontext) • [Demo](https://codesandbox.io/s/react-hook-form-v7-form-context-ytudi)
75
+ *
76
+ * @returns return all useForm methods
77
+ *
78
+ * @example
79
+ * ```tsx
80
+ * function App() {
81
+ * const methods = useForm();
82
+ * const onSubmit = data => console.log(data);
83
+ *
84
+ * return (
85
+ * <FormProvider {...methods} >
86
+ * <form onSubmit={methods.handleSubmit(onSubmit)}>
87
+ * <NestedInput />
88
+ * <input type="submit" />
89
+ * </form>
90
+ * </FormProvider>
91
+ * );
92
+ * }
93
+ *
94
+ * function NestedInput() {
95
+ * const { register } = useFormContext(); // retrieve all hook methods
96
+ * return <input {...register("test")} />;
97
+ * }
98
+ * ```
99
+ */
69
100
  const useFormContext = () => React.useContext(HookFormContext);
101
+ /**
102
+ * A provider component that propagates the `useForm` methods to all children components via [React Context](https://reactjs.org/docs/context.html) API. To be used with {@link useFormContext}.
103
+ *
104
+ * @remarks
105
+ * [API](https://react-hook-form.com/api/useformcontext) • [Demo](https://codesandbox.io/s/react-hook-form-v7-form-context-ytudi)
106
+ *
107
+ * @param props - all useFrom methods
108
+ *
109
+ * @example
110
+ * ```tsx
111
+ * function App() {
112
+ * const methods = useForm();
113
+ * const onSubmit = data => console.log(data);
114
+ *
115
+ * return (
116
+ * <FormProvider {...methods} >
117
+ * <form onSubmit={methods.handleSubmit(onSubmit)}>
118
+ * <NestedInput />
119
+ * <input type="submit" />
120
+ * </form>
121
+ * </FormProvider>
122
+ * );
123
+ * }
124
+ *
125
+ * function NestedInput() {
126
+ * const { register } = useFormContext(); // retrieve all hook methods
127
+ * return <input {...register("test")} />;
128
+ * }
129
+ * ```
130
+ */
70
131
  const FormProvider = (props) => (React.createElement(HookFormContext.Provider, { value: omit(props, 'children') }, props.children));
71
132
 
72
133
  var getProxyFormState = (formState, _proxyFormState, localProxyFormState, isRoot = true) => {
@@ -124,6 +185,36 @@ function useSubscribe(props) {
124
185
  }, [props.disabled]);
125
186
  }
126
187
 
188
+ /**
189
+ * This custom hook allows you to subscribe to each form state, and isolate the re-render at the custom hook level. It has its scope in terms of form state subscription, so it would not affect other useFormState and useForm. Using this hook can reduce the re-render impact on large and complex form application.
190
+ *
191
+ * @remarks
192
+ * [API](https://react-hook-form.com/api/useformstate) • [Demo](https://codesandbox.io/s/useformstate-75xly)
193
+ *
194
+ * @param props - include options on specify fields to subscribe. {@link UseFormStateReturn}
195
+ *
196
+ * @example
197
+ * ```tsx
198
+ * function App() {
199
+ * const { register, handleSubmit, control } = useForm({
200
+ * defaultValues: {
201
+ * firstName: "firstName"
202
+ * }});
203
+ * const { dirtyFields } = useFormState({
204
+ * control
205
+ * });
206
+ * const onSubmit = (data) => console.log(data);
207
+ *
208
+ * return (
209
+ * <form onSubmit={handleSubmit(onSubmit)}>
210
+ * <input {...register("firstName")} placeholder="First Name" />
211
+ * {dirtyFields.firstName && <p>Field is dirty.</p>}
212
+ * <input type="submit" />
213
+ * </form>
214
+ * );
215
+ * }
216
+ * ```
217
+ */
127
218
  function useFormState(props) {
128
219
  const methods = useFormContext();
129
220
  const { control = methods.control, disabled, name, exact } = props || {};
@@ -181,6 +272,22 @@ var objectHasFunction = (data) => {
181
272
  return false;
182
273
  };
183
274
 
275
+ /**
276
+ * Custom hook to subscribe to field change and isolate re-rendering at the component level.
277
+ *
278
+ * @remarks
279
+ *
280
+ * [API](https://react-hook-form.com/api/usewatch) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-usewatch-h9i5e)
281
+ *
282
+ * @example
283
+ * ```tsx
284
+ * const { watch } = useForm();
285
+ * const values = useWatch({
286
+ * name: "fieldName"
287
+ * control,
288
+ * })
289
+ * ```
290
+ */
184
291
  function useWatch(props) {
185
292
  const methods = useFormContext();
186
293
  const { control = methods.control, name, defaultValue, disabled, exact, } = props || {};
@@ -212,6 +319,30 @@ function useWatch(props) {
212
319
  return value;
213
320
  }
214
321
 
322
+ /**
323
+ * 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.
324
+ *
325
+ * @remarks
326
+ * [API](https://react-hook-form.com/api/usecontroller) • [Demo](https://codesandbox.io/s/usecontroller-0o8px)
327
+ *
328
+ * @param props - the path name to the form field value, and validation rules.
329
+ *
330
+ * @returns field properties, field and form state. {@link UseControllerReturn}
331
+ *
332
+ * @example
333
+ * ```tsx
334
+ * function Input(props) {
335
+ * const { field, fieldState, formState } = useController(props);
336
+ * return (
337
+ * <div>
338
+ * <input {...field} placeholder={props.name} />
339
+ * <p>{fieldState.isTouched && "Touched"}</p>
340
+ * <p>{formState.isSubmitted ? "submitted" : ""}</p>
341
+ * </div>
342
+ * );
343
+ * }
344
+ * ```
345
+ */
215
346
  function useController(props) {
216
347
  const methods = useFormContext();
217
348
  const { name, control = methods.control, shouldUnregister } = props;
@@ -220,7 +351,7 @@ function useController(props) {
220
351
  control,
221
352
  name,
222
353
  defaultValue: get(control._formValues, name, get(control._defaultValues, name, props.defaultValue)),
223
- exact: !isArrayField,
354
+ exact: true,
224
355
  });
225
356
  const formState = useFormState({
226
357
  control,
@@ -282,6 +413,48 @@ function useController(props) {
282
413
  };
283
414
  }
284
415
 
416
+ /**
417
+ * Component based on `useController` hook to work with controlled component.
418
+ *
419
+ * @remarks
420
+ * [API](https://react-hook-form.com/api/usecontroller/controller) • [Demo](https://codesandbox.io/s/react-hook-form-v6-controller-ts-jwyzw) • [Video](https://www.youtube.com/watch?v=N2UNk_UCVyA)
421
+ *
422
+ * @param props - the path name to the form field value, and validation rules.
423
+ *
424
+ * @returns provide field handler functions, field and form state.
425
+ *
426
+ * @example
427
+ * ```tsx
428
+ * function App() {
429
+ * const { control } = useForm<FormValues>({
430
+ * defaultValues: {
431
+ * test: ""
432
+ * }
433
+ * });
434
+ *
435
+ * return (
436
+ * <form>
437
+ * <Controller
438
+ * control={control}
439
+ * name="test"
440
+ * render={({ field: { onChange, onBlur, value, ref }, formState, fieldState }) => (
441
+ * <>
442
+ * <input
443
+ * onChange={onChange} // send value to hook form
444
+ * onBlur={onBlur} // notify when input is touched
445
+ * value={value} // return updated value
446
+ * ref={ref} // set ref for focus management
447
+ * />
448
+ * <p>{formState.isSubmitted ? "submitted" : ""}</p>
449
+ * <p>{fieldState.isTouched ? "touched" : ""}</p>
450
+ * </>
451
+ * )}
452
+ * />
453
+ * </form>
454
+ * );
455
+ * }
456
+ * ```
457
+ */
285
458
  const Controller = (props) => props.render(useController(props));
286
459
 
287
460
  var appendErrors = (name, validateAllFieldCriteria, errors, type, message) => validateAllFieldCriteria
@@ -431,7 +604,44 @@ var updateAt = (fieldValues, index, value) => {
431
604
  return fieldValues;
432
605
  };
433
606
 
434
- const useFieldArray = (props) => {
607
+ /**
608
+ * A custom hook that exposes convenient methods to perform operations with a list of dynamic inputs that need to be appended, updated, removed etc.
609
+ *
610
+ * @remarks
611
+ * [API](https://react-hook-form.com/api/usefieldarray) • [Demo](https://codesandbox.io/s/react-hook-form-usefieldarray-ssugn)
612
+ *
613
+ * @param props - useFieldArray props
614
+ *
615
+ * @returns methods - functions to manipulate with the Field Arrays (dynamic inputs) {@link UseFieldArrayReturn}
616
+ *
617
+ * @example
618
+ * ```tsx
619
+ * function App() {
620
+ * const { register, control, handleSubmit, reset, trigger, setError } = useForm({
621
+ * defaultValues: {
622
+ * test: []
623
+ * }
624
+ * });
625
+ * const { fields, append } = useFieldArray({
626
+ * control,
627
+ * name: "test"
628
+ * });
629
+ *
630
+ * return (
631
+ * <form onSubmit={handleSubmit(data => console.log(data))}>
632
+ * {fields.map((item, index) => (
633
+ * <input key={item.id} {...register(`test.${index}.firstName`)} />
634
+ * ))}
635
+ * <button type="button" onClick={() => append({ firstName: "bill" })}>
636
+ * append
637
+ * </button>
638
+ * <input type="submit" />
639
+ * </form>
640
+ * );
641
+ * }
642
+ * ```
643
+ */
644
+ function useFieldArray(props) {
435
645
  const methods = useFormContext();
436
646
  const { control = methods.control, name, keyName = 'id', shouldUnregister, } = props;
437
647
  const [fields, setFields] = React.useState(control._getFieldArray(name));
@@ -533,11 +743,11 @@ const useFieldArray = (props) => {
533
743
  }, true, false);
534
744
  };
535
745
  const replace = (value) => {
536
- const updatedFieldArrayValues = convertToArrayPayload(value);
746
+ const updatedFieldArrayValues = convertToArrayPayload(cloneObject(value));
537
747
  ids.current = updatedFieldArrayValues.map(generateId);
538
748
  updateValues([...updatedFieldArrayValues]);
539
749
  setFields([...updatedFieldArrayValues]);
540
- control._updateFieldArray(name, [...updatedFieldArrayValues], () => updatedFieldArrayValues, {}, true, false);
750
+ control._updateFieldArray(name, [...updatedFieldArrayValues], (data) => data, {}, true, false);
541
751
  };
542
752
  React.useEffect(() => {
543
753
  control._stateFlags.action = false;
@@ -580,7 +790,7 @@ const useFieldArray = (props) => {
580
790
  replace: React.useCallback(replace, [updateValues, name, control]),
581
791
  fields: React.useMemo(() => fields.map((field, index) => (Object.assign(Object.assign({}, field), { [keyName]: ids.current[index] || generateId() }))), [fields, keyName]),
582
792
  };
583
- };
793
+ }
584
794
 
585
795
  function createSubject() {
586
796
  let _observers = [];
@@ -969,8 +1179,7 @@ var validateField = async (field, inputValue, validateAllFieldCriteria, shouldUs
969
1179
  const maxOutput = getValueAndMessage(max);
970
1180
  const minOutput = getValueAndMessage(min);
971
1181
  if (!isNaN(inputValue)) {
972
- const valueNumber = ref.valueAsNumber ||
973
- parseFloat(inputValue);
1182
+ const valueNumber = ref.valueAsNumber || +inputValue;
974
1183
  if (!isNullOrUndefined(maxOutput.value)) {
975
1184
  exceedMax = valueNumber > maxOutput.value;
976
1185
  }
@@ -1238,7 +1447,8 @@ function createFormControl(props = {}) {
1238
1447
  _subjects.state.next(updatedFormState);
1239
1448
  }
1240
1449
  validateFields[name]--;
1241
- if (_proxyFormState.isValidating && !validateFields[name]) {
1450
+ if (_proxyFormState.isValidating &&
1451
+ !Object.values(validateFields).some((v) => v)) {
1242
1452
  _subjects.state.next({
1243
1453
  isValidating: false,
1244
1454
  });
@@ -1413,7 +1623,7 @@ function createFormControl(props = {}) {
1413
1623
  const fieldValue = target.type
1414
1624
  ? getFieldValue(field._f)
1415
1625
  : getEventValue(event);
1416
- const isBlurEvent = event.type === EVENTS.BLUR;
1626
+ const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
1417
1627
  const shouldSkipValidation = (!hasValidation(field._f) &&
1418
1628
  !_options.resolver &&
1419
1629
  !get(_formState.errors, name) &&
@@ -1616,9 +1826,7 @@ function createFormControl(props = {}) {
1616
1826
  e.persist && e.persist();
1617
1827
  }
1618
1828
  let hasNoPromiseError = true;
1619
- let fieldValues = _options.shouldUnregister
1620
- ? cloneObject(_formValues)
1621
- : Object.assign({}, _formValues);
1829
+ let fieldValues = cloneObject(_formValues);
1622
1830
  _subjects.state.next({
1623
1831
  isSubmitting: true,
1624
1832
  });
@@ -1640,7 +1848,9 @@ function createFormControl(props = {}) {
1640
1848
  await onValid(fieldValues, e);
1641
1849
  }
1642
1850
  else {
1643
- onInvalid && (await onInvalid(_formState.errors, e));
1851
+ if (onInvalid) {
1852
+ await onInvalid(Object.assign({}, _formState.errors), e);
1853
+ }
1644
1854
  _options.shouldFocusError &&
1645
1855
  focusFieldBy(_fields, (key) => get(_formState.errors, key), _names.mount);
1646
1856
  }
@@ -1830,6 +2040,35 @@ function createFormControl(props = {}) {
1830
2040
  };
1831
2041
  }
1832
2042
 
2043
+ /**
2044
+ * Custom hook to mange the entire form.
2045
+ *
2046
+ * @remarks
2047
+ * [API](https://react-hook-form.com/api/useform) • [Demo](https://codesandbox.io/s/react-hook-form-get-started-ts-5ksmm) • [Video](https://www.youtube.com/watch?v=RkXv4AXXC_4)
2048
+ *
2049
+ * @param props - form configuration and validation parameters.
2050
+ *
2051
+ * @returns methods - individual functions to manage the form state. {@link UseFormReturn}
2052
+ *
2053
+ * @example
2054
+ * ```tsx
2055
+ * function App() {
2056
+ * const { register, handleSubmit, watch, formState: { errors } } = useForm();
2057
+ * const onSubmit = data => console.log(data);
2058
+ *
2059
+ * console.log(watch("example"));
2060
+ *
2061
+ * return (
2062
+ * <form onSubmit={handleSubmit(onSubmit)}>
2063
+ * <input defaultValue="test" {...register("example")} />
2064
+ * <input {...register("exampleRequired", { required: true })} />
2065
+ * {errors.exampleRequired && <span>This field is required</span>}
2066
+ * <input type="submit" />
2067
+ * </form>
2068
+ * );
2069
+ * }
2070
+ * ```
2071
+ */
1833
2072
  function useForm(props = {}) {
1834
2073
  const _formControl = React.useRef();
1835
2074
  const [formState, updateFormState] = React.useState({