react-hook-form 7.76.0 → 7.77.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.
@@ -18,15 +18,9 @@ var getEventValue = (event) => isObject(event) && event.target
18
18
  : event.target.value
19
19
  : event;
20
20
 
21
- var getFieldArrayParentNames = (names, name) => {
22
- const parts = name.split('.');
23
- const matches = [];
24
- let prefix = parts[0];
25
- for (let i = 1; i < parts.length; prefix += '.' + parts[i++]) {
26
- !isNaN(+parts[i]) && names.has(prefix) && matches.push(prefix);
27
- }
28
- return matches;
29
- };
21
+ var isNameInFieldArray = (names, name) => name
22
+ .split('.')
23
+ .some((part, index, arr) => !isNaN(Number(part)) && names.has(arr.slice(0, index).join('.')));
30
24
 
31
25
  var isPlainObject = (tempObject) => {
32
26
  const prototypeCopy = tempObject.constructor && tempObject.constructor.prototype;
@@ -58,19 +52,48 @@ function cloneObject(data) {
58
52
  return copy;
59
53
  }
60
54
 
55
+ const EVENTS = {
56
+ BLUR: 'blur',
57
+ FOCUS_OUT: 'focusout',
58
+ CHANGE: 'change',
59
+ SUBMIT: 'submit',
60
+ TRIGGER: 'trigger',
61
+ VALID: 'valid',
62
+ };
63
+ const VALIDATION_MODE = {
64
+ onBlur: 'onBlur',
65
+ onChange: 'onChange',
66
+ onSubmit: 'onSubmit',
67
+ onTouched: 'onTouched',
68
+ all: 'all',
69
+ };
70
+ const INPUT_VALIDATION_RULES = {
71
+ max: 'max',
72
+ min: 'min',
73
+ maxLength: 'maxLength',
74
+ minLength: 'minLength',
75
+ pattern: 'pattern',
76
+ required: 'required',
77
+ validate: 'validate',
78
+ };
79
+ const FORM_ERROR_TYPE = 'form';
80
+ const ROOT_ERROR_TYPE = 'root';
81
+ const PROTOTYPE_KEYWORDS = ['__proto__', 'constructor', 'prototype'];
82
+
61
83
  var isKey = (value) => /^\w*$/.test(value);
62
84
 
63
85
  var isUndefined = (val) => val === undefined;
64
86
 
65
- var compact = (value) => Array.isArray(value) ? value.filter(Boolean) : [];
66
-
67
- var stringToPath = (input) => compact(input.replace(/["|']|\]/g, '').split(/\.|\[/));
87
+ var stringToPath = (input) => input.split(/[.[\]'"]/g).filter(Boolean);
68
88
 
69
89
  var get = (object, path, defaultValue) => {
70
90
  if (!path || !isObject(object)) {
71
91
  return defaultValue;
72
92
  }
73
93
  const paths = isKey(path) ? [path] : stringToPath(path);
94
+ if (paths.some((key) => PROTOTYPE_KEYWORDS.includes(key))) {
95
+ return defaultValue;
96
+ }
74
97
  const result = paths.reduce((result, key) => {
75
98
  return isNullOrUndefined(result) ? undefined : result[key];
76
99
  }, object);
@@ -102,7 +125,7 @@ var set = (object, path, value) => {
102
125
  ? []
103
126
  : {};
104
127
  }
105
- if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
128
+ if (PROTOTYPE_KEYWORDS.includes(key)) {
106
129
  return;
107
130
  }
108
131
  object[key] = newValue;
@@ -110,33 +133,6 @@ var set = (object, path, value) => {
110
133
  }
111
134
  };
112
135
 
113
- const EVENTS = {
114
- BLUR: 'blur',
115
- FOCUS_OUT: 'focusout',
116
- CHANGE: 'change',
117
- SUBMIT: 'submit',
118
- TRIGGER: 'trigger',
119
- VALID: 'valid',
120
- };
121
- const VALIDATION_MODE = {
122
- onBlur: 'onBlur',
123
- onChange: 'onChange',
124
- onSubmit: 'onSubmit',
125
- onTouched: 'onTouched',
126
- all: 'all',
127
- };
128
- const INPUT_VALIDATION_RULES = {
129
- max: 'max',
130
- min: 'min',
131
- maxLength: 'maxLength',
132
- minLength: 'minLength',
133
- pattern: 'pattern',
134
- required: 'required',
135
- validate: 'validate',
136
- };
137
- const FORM_ERROR_TYPE = 'form';
138
- const ROOT_ERROR_TYPE = 'root';
139
-
140
136
  /**
141
137
  * Separate context for `control` to prevent unnecessary rerenders.
142
138
  * Internal hooks that only need control use this instead of full form context.
@@ -165,7 +161,9 @@ var getProxyFormState = (formState, control, localProxyFormState, isRoot = true)
165
161
  return result;
166
162
  };
167
163
 
168
- const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
164
+ const useIsomorphicLayoutEffect = isWeb
165
+ ? React.useLayoutEffect
166
+ : React.useEffect;
169
167
 
170
168
  /**
171
169
  * 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.
@@ -402,8 +400,7 @@ function useWatch(props) {
402
400
  function useController(props) {
403
401
  const formControl = useFormControlContext();
404
402
  const { name, disabled, control = formControl, shouldUnregister, defaultValue, exact = true, } = props;
405
- const isArrayField = !!getFieldArrayParentNames(control._names.array, name)
406
- .length;
403
+ const isArrayField = isNameInFieldArray(control._names.array, name);
407
404
  const defaultValueMemo = React.useMemo(() => get(control._formValues, name, get(control._defaultValues, name, defaultValue)), [control, name, defaultValue]);
408
405
  const value = useWatch({
409
406
  control,
@@ -647,8 +644,7 @@ const useFormContext = () => React.useContext(HookFormContext);
647
644
  * }
648
645
  * ```
649
646
  */
650
- const FormProvider = (props) => {
651
- const { children, watch, getValues, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, handleSubmit, unregister, control, register, setFocus, subscribe, } = props;
647
+ const FormProvider = ({ children, watch, getValues, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, handleSubmit, unregister, control, register, setFocus, subscribe, }) => {
652
648
  const memoizedValue = React.useMemo(() => ({
653
649
  watch,
654
650
  getValues,
@@ -813,6 +809,8 @@ var appendErrors = (name, validateAllFieldCriteria, errors, type, message) => va
813
809
  }
814
810
  : {};
815
811
 
812
+ var compact = (value) => Array.isArray(value) ? value.filter(Boolean) : [];
813
+
816
814
  var convertToArrayPayload = (value) => (Array.isArray(value) ? value : [value]);
817
815
 
818
816
  var createSubject = () => {
@@ -1122,8 +1120,7 @@ var hasValidation = (options) => options.mount &&
1122
1120
  var isWatched = (name, _names, isBlurEvent) => !isBlurEvent &&
1123
1121
  (_names.watchAll ||
1124
1122
  _names.watch.has(name) ||
1125
- [..._names.watch].some((watchName) => name.startsWith(watchName) &&
1126
- /^\.\w+/.test(name.slice(watchName.length))));
1123
+ [..._names.watch].some((watchName) => name.startsWith(`${watchName}.`)));
1127
1124
 
1128
1125
  const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
1129
1126
  for (const key of fieldsNames || Object.keys(fields)) {
@@ -1226,7 +1223,8 @@ var skipValidation = (isBlurEvent, isTouched, isSubmitted, reValidateMode, mode)
1226
1223
  var unsetEmptyArray = (ref, name) => !compact(get(ref, name)).length && unset(ref, name);
1227
1224
 
1228
1225
  var updateFieldArrayRootError = (errors, error, name) => {
1229
- const fieldArrayErrors = convertToArrayPayload(get(errors, name));
1226
+ const existingErrors = get(errors, name);
1227
+ const fieldArrayErrors = Array.isArray(existingErrors) ? existingErrors : [];
1230
1228
  set(fieldArrayErrors, ROOT_ERROR_TYPE, error[name]);
1231
1229
  set(errors, name, fieldArrayErrors);
1232
1230
  return errors;
@@ -1273,8 +1271,7 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
1273
1271
  isUndefined(inputValue)) ||
1274
1272
  (isHTMLElement(ref) && ref.value === '') ||
1275
1273
  inputValue === '' ||
1276
- (Array.isArray(inputValue) && !inputValue.length) ||
1277
- (valueAsNumber && typeof inputValue === 'number' && isNaN(inputValue));
1274
+ (Array.isArray(inputValue) && !inputValue.length);
1278
1275
  const appendErrorsCurry = appendErrors.bind(null, name, validateAllFieldCriteria, error);
1279
1276
  const getMinMaxMessage = (exceedMax, maxLengthMessage, minLengthMessage, maxType = INPUT_VALIDATION_RULES.maxLength, minType = INPUT_VALIDATION_RULES.minLength) => {
1280
1277
  const message = exceedMax ? maxLengthMessage : minLengthMessage;
@@ -1742,7 +1739,9 @@ function createFormControl(props = {}) {
1742
1739
  for (const name of names) {
1743
1740
  const error = get(errors, name);
1744
1741
  error
1745
- ? _names.array.has(name) && isObject(error)
1742
+ ? _names.array.has(name) &&
1743
+ isObject(error) &&
1744
+ !Object.keys(error).some((key) => !Number.isNaN(Number(key)))
1746
1745
  ? updateFieldArrayRootError(_formState.errors, { [name]: error }, name)
1747
1746
  : set(_formState.errors, name, error)
1748
1747
  : unset(_formState.errors, name);
@@ -1872,7 +1871,7 @@ function createFormControl(props = {}) {
1872
1871
  : defaultValue),
1873
1872
  }, isGlobal, defaultValue);
1874
1873
  const _getFieldArray = (name) => compact(get(_state.mount ? _formValues : _defaultValues, name, _options.shouldUnregister ? get(_defaultValues, name, []) : []));
1875
- const setFieldValue = (name, value, options = {}) => {
1874
+ const setFieldValue = (name, value, options = {}, skipClone = false) => {
1876
1875
  const field = get(_fields, name);
1877
1876
  let fieldValue = value;
1878
1877
  if (field) {
@@ -1913,7 +1912,7 @@ function createFormControl(props = {}) {
1913
1912
  if (!fieldReference.ref.type) {
1914
1913
  _subjects.state.next({
1915
1914
  name,
1916
- values: cloneObject(_formValues),
1915
+ values: skipClone ? _formValues : cloneObject(_formValues),
1917
1916
  });
1918
1917
  }
1919
1918
  }
@@ -1923,7 +1922,7 @@ function createFormControl(props = {}) {
1923
1922
  updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty, true);
1924
1923
  options.shouldValidate && trigger(name);
1925
1924
  };
1926
- const setFieldValues = (name, value, options) => {
1925
+ const setFieldValues = (name, value, options, skipClone = false) => {
1927
1926
  for (const fieldKey in value) {
1928
1927
  if (!value.hasOwnProperty(fieldKey)) {
1929
1928
  return;
@@ -1935,14 +1934,14 @@ function createFormControl(props = {}) {
1935
1934
  isObject(fieldValue) ||
1936
1935
  (field && !field._f)) &&
1937
1936
  !isDateObject(fieldValue)
1938
- ? setFieldValues(fieldName, fieldValue, options)
1939
- : setFieldValue(fieldName, fieldValue, options);
1937
+ ? setFieldValues(fieldName, fieldValue, options, skipClone)
1938
+ : setFieldValue(fieldName, fieldValue, options, skipClone);
1940
1939
  }
1941
1940
  };
1942
- const setValue = (name, value, options = {}) => {
1941
+ const _setValue = (name, value, options, skipClone) => {
1943
1942
  const field = get(_fields, name);
1944
1943
  const isFieldArray = _names.array.has(name);
1945
- const cloneValue = cloneObject(value);
1944
+ const cloneValue = skipClone ? value : cloneObject(value);
1946
1945
  const previousValue = get(_formValues, name);
1947
1946
  const isValueUnchanged = deepEqual(previousValue, cloneValue);
1948
1947
  if (!isValueUnchanged) {
@@ -1951,7 +1950,7 @@ function createFormControl(props = {}) {
1951
1950
  if (isFieldArray) {
1952
1951
  _subjects.array.next({
1953
1952
  name,
1954
- values: cloneObject(_formValues),
1953
+ values: skipClone ? _formValues : cloneObject(_formValues),
1955
1954
  });
1956
1955
  if ((_proxyFormState.isDirty ||
1957
1956
  _proxyFormState.dirtyFields ||
@@ -1970,20 +1969,15 @@ function createFormControl(props = {}) {
1970
1969
  const isEmpty = (Array.isArray(cloneValue) && !cloneValue.length) ||
1971
1970
  isEmptyObject(cloneValue);
1972
1971
  if (!field || field._f || isNullOrUndefined(cloneValue) || isEmpty) {
1973
- setFieldValue(name, cloneValue, options);
1972
+ setFieldValue(name, cloneValue, options, skipClone);
1974
1973
  }
1975
1974
  else {
1976
- setFieldValues(name, cloneValue, options);
1975
+ setFieldValues(name, cloneValue, options, skipClone);
1977
1976
  }
1978
1977
  }
1979
1978
  if (!isValueUnchanged) {
1980
1979
  const watched = isWatched(name, _names);
1981
- const values = cloneObject(_formValues);
1982
- if (!isFieldArray) {
1983
- for (const arrayName of getFieldArrayParentNames(_names.array, name)) {
1984
- _subjects.array.next({ name: arrayName, values });
1985
- }
1986
- }
1980
+ const values = skipClone ? _formValues : cloneObject(_formValues);
1987
1981
  _subjects.state.next({
1988
1982
  ...(watched && _formState),
1989
1983
  name: _state.mount || watched ? name : undefined,
@@ -1991,7 +1985,8 @@ function createFormControl(props = {}) {
1991
1985
  });
1992
1986
  }
1993
1987
  };
1994
- const setValues = (formValues) => {
1988
+ const setValue = (name, value, options = {}) => _setValue(name, value, options, false);
1989
+ const setValues = (formValues, options = {}) => {
1995
1990
  const updatedFormValues = isFunction(formValues)
1996
1991
  ? formValues(_formValues)
1997
1992
  : formValues;
@@ -2001,9 +1996,17 @@ function createFormControl(props = {}) {
2001
1996
  ...updatedFormValues,
2002
1997
  };
2003
1998
  for (const fieldName of _names.mount) {
2004
- setValue(fieldName, get(updatedFormValues, fieldName));
1999
+ _setValue(fieldName, get(updatedFormValues, fieldName), options, true);
2000
+ }
2001
+ _subjects.state.next({
2002
+ ..._formState,
2003
+ name: undefined,
2004
+ type: undefined,
2005
+ values: _formValues,
2006
+ });
2007
+ if (options.shouldValidate) {
2008
+ _setValid();
2005
2009
  }
2006
- _subjects.state.next({ ..._formState, values: _formValues });
2007
2010
  }
2008
2011
  };
2009
2012
  const onChange = async (event) => {
@@ -2373,8 +2376,7 @@ function createFormControl(props = {}) {
2373
2376
  field._f.mount = false;
2374
2377
  }
2375
2378
  (_options.shouldUnregister || options.shouldUnregister) &&
2376
- !(getFieldArrayParentNames(_names.array, name).length &&
2377
- _state.action) &&
2379
+ !(isNameInFieldArray(_names.array, name) && _state.action) &&
2378
2380
  _names.unMount.add(name);
2379
2381
  }
2380
2382
  },
@@ -2486,7 +2488,7 @@ function createFormControl(props = {}) {
2486
2488
  const updatedValues = formValues ? cloneObject(formValues) : _defaultValues;
2487
2489
  const cloneUpdatedValues = cloneObject(updatedValues);
2488
2490
  const isEmptyResetValues = isEmptyObject(formValues);
2489
- const values = isEmptyResetValues ? _defaultValues : cloneUpdatedValues;
2491
+ const values = cloneUpdatedValues;
2490
2492
  if (!keepStateOptions.keepDefaultValues) {
2491
2493
  _defaultValues = updatedValues;
2492
2494
  }
@@ -2535,11 +2537,19 @@ function createFormControl(props = {}) {
2535
2537
  _fields = {};
2536
2538
  }
2537
2539
  }
2538
- _formValues = _options.shouldUnregister
2539
- ? keepStateOptions.keepDefaultValues
2540
+ if (_options.shouldUnregister) {
2541
+ _formValues = keepStateOptions.keepDefaultValues
2540
2542
  ? cloneObject(_defaultValues)
2541
- : {}
2542
- : cloneObject(values);
2543
+ : {};
2544
+ if (keepStateOptions.keepFieldsRef) {
2545
+ for (const fieldName of _names.mount) {
2546
+ set(_formValues, fieldName, get(values, fieldName));
2547
+ }
2548
+ }
2549
+ }
2550
+ else {
2551
+ _formValues = cloneObject(values);
2552
+ }
2543
2553
  _subjects.array.next({
2544
2554
  values: { ...values },
2545
2555
  });
@@ -2579,8 +2589,10 @@ function createFormControl(props = {}) {
2579
2589
  ? false
2580
2590
  : keepStateOptions.keepDirty
2581
2591
  ? _formState.isDirty
2582
- : !!(keepStateOptions.keepDefaultValues &&
2583
- !deepEqual(formValues, _defaultValues)),
2592
+ : keepStateOptions.keepValues
2593
+ ? _getDirty()
2594
+ : !!(keepStateOptions.keepDefaultValues &&
2595
+ !deepEqual(formValues, _defaultValues)),
2584
2596
  isSubmitted: keepStateOptions.keepIsSubmitted
2585
2597
  ? _formState.isSubmitted
2586
2598
  : false,
@@ -2641,6 +2653,21 @@ function createFormControl(props = {}) {
2641
2653
  isLoading: false,
2642
2654
  });
2643
2655
  });
2656
+ const resetDefaultValues = (values, options = {}) => {
2657
+ _defaultValues = cloneObject(values);
2658
+ if (!options.keepDirty) {
2659
+ const newDirtyFields = getDirtyFields(_defaultValues, _formValues);
2660
+ _formState.dirtyFields = newDirtyFields;
2661
+ _formState.isDirty = !isEmptyObject(newDirtyFields);
2662
+ }
2663
+ if (!options.keepIsValid) {
2664
+ _setValid();
2665
+ }
2666
+ _subjects.state.next({
2667
+ ..._formState,
2668
+ defaultValues: _defaultValues,
2669
+ });
2670
+ };
2644
2671
  const methods = {
2645
2672
  control: {
2646
2673
  register,
@@ -2709,6 +2736,7 @@ function createFormControl(props = {}) {
2709
2736
  getValues,
2710
2737
  reset,
2711
2738
  resetField,
2739
+ resetDefaultValues,
2712
2740
  clearErrors,
2713
2741
  unregister,
2714
2742
  setError,
@@ -2845,6 +2873,10 @@ function useFieldArray(props) {
2845
2873
  setFields(fieldValues);
2846
2874
  ids.current = fieldValues.map(generateId);
2847
2875
  }
2876
+ else if (!fieldArrayName) {
2877
+ setFields([]);
2878
+ ids.current = [];
2879
+ }
2848
2880
  }
2849
2881
  },
2850
2882
  }).unsubscribe, [control, name]);