react-hook-form 7.71.2 → 7.72.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.
@@ -18,9 +18,9 @@ var getEventValue = (event) => isObject(event) && event.target
18
18
  : event.target.value
19
19
  : event;
20
20
 
21
- var getNodeParentName = (name) => name.substring(0, name.search(/\.\d+(\.|$)/)) || name;
22
-
23
- var isNameInFieldArray = (names, name) => names.has(getNodeParentName(name));
21
+ var isNameInFieldArray = (names, name) => name
22
+ .split('.')
23
+ .some((part, index, arr) => !isNaN(Number(part)) && names.has(arr.slice(0, index).join('.')));
24
24
 
25
25
  var isPlainObject = (tempObject) => {
26
26
  const prototypeCopy = tempObject.constructor && tempObject.constructor.prototype;
@@ -105,6 +105,9 @@ const EVENTS = {
105
105
  BLUR: 'blur',
106
106
  FOCUS_OUT: 'focusout',
107
107
  CHANGE: 'change',
108
+ SUBMIT: 'submit',
109
+ TRIGGER: 'trigger',
110
+ VALID: 'valid',
108
111
  };
109
112
  const VALIDATION_MODE = {
110
113
  onBlur: 'onBlur',
@@ -122,6 +125,8 @@ const INPUT_VALIDATION_RULES = {
122
125
  required: 'required',
123
126
  validate: 'validate',
124
127
  };
128
+ const FORM_ERROR_TYPE = 'form';
129
+ const ROOT_ERROR_TYPE = 'root';
125
130
 
126
131
  /**
127
132
  * Separate context for `control` to prevent unnecessary rerenders.
@@ -259,8 +264,8 @@ function deepEqual(object1, object2, _internal_visited = new WeakSet()) {
259
264
  if (key !== 'ref') {
260
265
  const val2 = object2[key];
261
266
  if ((isDateObject(val1) && isDateObject(val2)) ||
262
- (isObject(val1) && isObject(val2)) ||
263
- (Array.isArray(val1) && Array.isArray(val2))
267
+ ((isObject(val1) || Array.isArray(val1)) &&
268
+ (isObject(val2) || Array.isArray(val2)))
264
269
  ? !deepEqual(val1, val2, _internal_visited)
265
270
  : !Object.is(val1, val2)) {
266
271
  return false;
@@ -635,42 +640,43 @@ const useFormContext = () => React.useContext(HookFormContext);
635
640
  */
636
641
  const FormProvider = (props) => {
637
642
  const { children, watch, getValues, getFieldState, setError, clearErrors, setValue, trigger, formState, resetField, reset, handleSubmit, unregister, control, register, setFocus, subscribe, } = props;
638
- return (React.createElement(HookFormContext.Provider, { value: React.useMemo(() => ({
639
- watch,
640
- getValues,
641
- getFieldState,
642
- setError,
643
- clearErrors,
644
- setValue,
645
- trigger,
646
- formState,
647
- resetField,
648
- reset,
649
- handleSubmit,
650
- unregister,
651
- control,
652
- register,
653
- setFocus,
654
- subscribe,
655
- }), [
656
- clearErrors,
657
- control,
658
- formState,
659
- getFieldState,
660
- getValues,
661
- handleSubmit,
662
- register,
663
- reset,
664
- resetField,
665
- setError,
666
- setFocus,
667
- setValue,
668
- subscribe,
669
- trigger,
670
- unregister,
671
- watch,
672
- ]) },
673
- React.createElement(HookFormControlContext.Provider, { value: control }, children)));
643
+ const memoizedValue = React.useMemo(() => ({
644
+ watch,
645
+ getValues,
646
+ getFieldState,
647
+ setError,
648
+ clearErrors,
649
+ setValue,
650
+ trigger,
651
+ formState,
652
+ resetField,
653
+ reset,
654
+ handleSubmit,
655
+ unregister,
656
+ control,
657
+ register,
658
+ setFocus,
659
+ subscribe,
660
+ }), [
661
+ clearErrors,
662
+ control,
663
+ formState,
664
+ getFieldState,
665
+ getValues,
666
+ handleSubmit,
667
+ register,
668
+ reset,
669
+ resetField,
670
+ setError,
671
+ setFocus,
672
+ setValue,
673
+ subscribe,
674
+ trigger,
675
+ unregister,
676
+ watch,
677
+ ]);
678
+ return (React.createElement(HookFormContext.Provider, { value: memoizedValue },
679
+ React.createElement(HookFormControlContext.Provider, { value: memoizedValue.control }, children)));
674
680
  };
675
681
 
676
682
  const POST_REQUEST = 'post';
@@ -1009,6 +1015,8 @@ function getFieldValue(_f) {
1009
1015
  return getFieldValueAs(isUndefined(ref.value) ? _f.ref.value : ref.value, _f);
1010
1016
  }
1011
1017
 
1018
+ var getNodeParentName = (name) => name.substring(0, name.search(/\.\d+(\.|$)/)) || name;
1019
+
1012
1020
  var getResolverOptions = (fieldsNames, _fields, criteriaMode, shouldUseNativeValidation) => {
1013
1021
  const fields = {};
1014
1022
  for (const name of fieldsNames) {
@@ -1167,7 +1175,7 @@ var unsetEmptyArray = (ref, name) => !compact(get(ref, name)).length && unset(re
1167
1175
 
1168
1176
  var updateFieldArrayRootError = (errors, error, name) => {
1169
1177
  const fieldArrayErrors = convertToArrayPayload(get(errors, name));
1170
- set(fieldArrayErrors, 'root', error[name]);
1178
+ set(fieldArrayErrors, ROOT_ERROR_TYPE, error[name]);
1171
1179
  set(errors, name, fieldArrayErrors);
1172
1180
  return errors;
1173
1181
  };
@@ -1415,6 +1423,7 @@ function createFormControl(props = {}) {
1415
1423
  unMount: new Set(),
1416
1424
  array: new Set(),
1417
1425
  watch: new Set(),
1426
+ registerName: new Set(),
1418
1427
  };
1419
1428
  let delayErrorCallback;
1420
1429
  let timer = 0;
@@ -1456,7 +1465,11 @@ function createFormControl(props = {}) {
1456
1465
  _updateIsValidating();
1457
1466
  }
1458
1467
  else {
1459
- isValid = await executeBuiltInValidation(_fields, true);
1468
+ isValid = await executeBuiltInValidation({
1469
+ fields: _fields,
1470
+ onlyCheckValid: true,
1471
+ eventType: EVENTS.VALID,
1472
+ });
1460
1473
  }
1461
1474
  if (isValid !== _formState.isValid) {
1462
1475
  _subjects.state.next({
@@ -1484,6 +1497,11 @@ function createFormControl(props = {}) {
1484
1497
  });
1485
1498
  }
1486
1499
  };
1500
+ const _updateDirtyFields = (name) => {
1501
+ const fullDirtyFields = getDirtyFields(_defaultValues, _formValues);
1502
+ const rootName = getNodeParentName(name);
1503
+ set(_formState.dirtyFields, rootName, get(fullDirtyFields, rootName));
1504
+ };
1487
1505
  const _setFieldArray = (name, values = [], method, args, shouldSetValues = true, shouldUpdateFieldsAndState = true) => {
1488
1506
  if (args && method && !_options.disabled) {
1489
1507
  _state.action = true;
@@ -1505,7 +1523,7 @@ function createFormControl(props = {}) {
1505
1523
  shouldSetValues && set(_formState.touchedFields, name, touchedFields);
1506
1524
  }
1507
1525
  if (_proxyFormState.dirtyFields || _proxySubscribeFormState.dirtyFields) {
1508
- _formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
1526
+ _updateDirtyFields(name);
1509
1527
  }
1510
1528
  _subjects.state.next({
1511
1529
  name,
@@ -1619,8 +1637,7 @@ function createFormControl(props = {}) {
1619
1637
  };
1620
1638
  const _runSchema = async (name) => {
1621
1639
  _updateIsValidating(name, true);
1622
- const result = await _options.resolver(_formValues, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation));
1623
- return result;
1640
+ return await _options.resolver(_formValues, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation));
1624
1641
  };
1625
1642
  const executeSchemaAndUpdateState = async (names) => {
1626
1643
  const { errors } = await _runSchema(names);
@@ -1638,9 +1655,55 @@ function createFormControl(props = {}) {
1638
1655
  }
1639
1656
  return errors;
1640
1657
  };
1641
- const executeBuiltInValidation = async (fields, shouldOnlyCheckValid, context = {
1658
+ const validateForm = async ({ name, eventType, }) => {
1659
+ if (props.validate) {
1660
+ const result = await props.validate({
1661
+ formValues: _formValues,
1662
+ formState: _formState,
1663
+ name,
1664
+ eventType,
1665
+ });
1666
+ if (isObject(result)) {
1667
+ for (const key in result) {
1668
+ const error = result[key];
1669
+ if (error) {
1670
+ setError(`${FORM_ERROR_TYPE}.${key}`, {
1671
+ message: isString(result.message) ? result.message : '',
1672
+ type: INPUT_VALIDATION_RULES.validate,
1673
+ });
1674
+ }
1675
+ }
1676
+ }
1677
+ else if (isString(result) || !result) {
1678
+ setError(FORM_ERROR_TYPE, {
1679
+ message: result || '',
1680
+ type: INPUT_VALIDATION_RULES.validate,
1681
+ });
1682
+ }
1683
+ else {
1684
+ clearErrors(FORM_ERROR_TYPE);
1685
+ }
1686
+ return result;
1687
+ }
1688
+ return true;
1689
+ };
1690
+ const executeBuiltInValidation = async ({ fields, onlyCheckValid, name, eventType, context = {
1642
1691
  valid: true,
1643
- }) => {
1692
+ runRootValidation: false,
1693
+ }, }) => {
1694
+ if (props.validate) {
1695
+ context.runRootValidation = true;
1696
+ const result = await validateForm({
1697
+ name,
1698
+ eventType,
1699
+ });
1700
+ if (!result) {
1701
+ context.valid = false;
1702
+ if (onlyCheckValid) {
1703
+ return context.valid;
1704
+ }
1705
+ }
1706
+ }
1644
1707
  for (const name in fields) {
1645
1708
  const field = fields[name];
1646
1709
  if (field) {
@@ -1651,25 +1714,34 @@ function createFormControl(props = {}) {
1651
1714
  if (isPromiseFunction && _proxyFormState.validatingFields) {
1652
1715
  _updateIsValidating([_f.name], true);
1653
1716
  }
1654
- const fieldError = await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !shouldOnlyCheckValid, isFieldArrayRoot);
1717
+ const fieldError = await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !onlyCheckValid, isFieldArrayRoot);
1655
1718
  if (isPromiseFunction && _proxyFormState.validatingFields) {
1656
1719
  _updateIsValidating([_f.name]);
1657
1720
  }
1658
1721
  if (fieldError[_f.name]) {
1659
1722
  context.valid = false;
1660
- if (shouldOnlyCheckValid || props.shouldUseNativeValidation) {
1723
+ if (onlyCheckValid) {
1661
1724
  break;
1662
1725
  }
1663
1726
  }
1664
- !shouldOnlyCheckValid &&
1727
+ !onlyCheckValid &&
1665
1728
  (get(fieldError, _f.name)
1666
1729
  ? isFieldArrayRoot
1667
1730
  ? updateFieldArrayRootError(_formState.errors, fieldError, _f.name)
1668
1731
  : set(_formState.errors, _f.name, fieldError[_f.name])
1669
1732
  : unset(_formState.errors, _f.name));
1733
+ if (props.shouldUseNativeValidation && fieldError[_f.name]) {
1734
+ break;
1735
+ }
1670
1736
  }
1671
1737
  !isEmptyObject(fieldValue) &&
1672
- (await executeBuiltInValidation(fieldValue, shouldOnlyCheckValid, context));
1738
+ (await executeBuiltInValidation({
1739
+ context,
1740
+ onlyCheckValid,
1741
+ fields: fieldValue,
1742
+ name: name,
1743
+ eventType,
1744
+ }));
1673
1745
  }
1674
1746
  }
1675
1747
  return context.valid;
@@ -1780,9 +1852,10 @@ function createFormControl(props = {}) {
1780
1852
  _proxySubscribeFormState.isDirty ||
1781
1853
  _proxySubscribeFormState.dirtyFields) &&
1782
1854
  options.shouldDirty) {
1855
+ _updateDirtyFields(name);
1783
1856
  _subjects.state.next({
1784
1857
  name,
1785
- dirtyFields: getDirtyFields(_defaultValues, _formValues),
1858
+ dirtyFields: _formState.dirtyFields,
1786
1859
  isDirty: _getDirty(name, cloneValue),
1787
1860
  });
1788
1861
  }
@@ -1828,6 +1901,7 @@ function createFormControl(props = {}) {
1828
1901
  : getEventValue(event);
1829
1902
  const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
1830
1903
  const shouldSkipValidation = (!hasValidation(field._f) &&
1904
+ !props.validate &&
1831
1905
  !_options.resolver &&
1832
1906
  !get(_formState.errors, name) &&
1833
1907
  !field._f.deps) ||
@@ -1865,6 +1939,12 @@ function createFormControl(props = {}) {
1865
1939
  return (shouldRender &&
1866
1940
  _subjects.state.next({ name, ...(watched ? {} : fieldState) }));
1867
1941
  }
1942
+ if (!_options.resolver && props.validate) {
1943
+ await validateForm({
1944
+ name: name,
1945
+ eventType: event.type,
1946
+ });
1947
+ }
1868
1948
  !isBlurEvent && watched && _subjects.state.next({ ..._formState });
1869
1949
  if (_options.resolver) {
1870
1950
  const { errors } = await _runSchema([name]);
@@ -1889,7 +1969,12 @@ function createFormControl(props = {}) {
1889
1969
  }
1890
1970
  else if (_proxyFormState.isValid ||
1891
1971
  _proxySubscribeFormState.isValid) {
1892
- isValid = await executeBuiltInValidation(_fields, true);
1972
+ isValid = await executeBuiltInValidation({
1973
+ fields: _fields,
1974
+ onlyCheckValid: true,
1975
+ name: name,
1976
+ eventType: event.type,
1977
+ });
1893
1978
  }
1894
1979
  }
1895
1980
  }
@@ -1922,12 +2007,19 @@ function createFormControl(props = {}) {
1922
2007
  else if (name) {
1923
2008
  validationResult = (await Promise.all(fieldNames.map(async (fieldName) => {
1924
2009
  const field = get(_fields, fieldName);
1925
- return await executeBuiltInValidation(field && field._f ? { [fieldName]: field } : field);
2010
+ return await executeBuiltInValidation({
2011
+ fields: field && field._f ? { [fieldName]: field } : field,
2012
+ eventType: EVENTS.TRIGGER,
2013
+ });
1926
2014
  }))).every(Boolean);
1927
2015
  !(!validationResult && !_formState.isValid) && _setValid();
1928
2016
  }
1929
2017
  else {
1930
- validationResult = isValid = await executeBuiltInValidation(_fields);
2018
+ validationResult = isValid = await executeBuiltInValidation({
2019
+ fields: _fields,
2020
+ name,
2021
+ eventType: EVENTS.TRIGGER,
2022
+ });
1931
2023
  }
1932
2024
  _subjects.state.next({
1933
2025
  ...(!isString(name) ||
@@ -2073,6 +2165,7 @@ function createFormControl(props = {}) {
2073
2165
  const register = (name, options = {}) => {
2074
2166
  let field = get(_fields, name);
2075
2167
  const disabledIsDefined = isBoolean(options.disabled) || isBoolean(_options.disabled);
2168
+ const shouldRevalidateRemount = !_names.registerName.has(name) && field && !field._f.mount;
2076
2169
  set(_fields, name, {
2077
2170
  ...(field || {}),
2078
2171
  _f: {
@@ -2083,7 +2176,7 @@ function createFormControl(props = {}) {
2083
2176
  },
2084
2177
  });
2085
2178
  _names.mount.add(name);
2086
- if (field) {
2179
+ if (field && !shouldRevalidateRemount) {
2087
2180
  _setDisabledField({
2088
2181
  disabled: isBoolean(options.disabled)
2089
2182
  ? options.disabled
@@ -2113,7 +2206,9 @@ function createFormControl(props = {}) {
2113
2206
  onBlur: onChange,
2114
2207
  ref: (ref) => {
2115
2208
  if (ref) {
2209
+ _names.registerName.add(name);
2116
2210
  register(name, options);
2211
+ _names.registerName.delete(name);
2117
2212
  field = get(_fields, name);
2118
2213
  const fieldRef = isUndefined(ref.value)
2119
2214
  ? ref.querySelectorAll
@@ -2192,14 +2287,17 @@ function createFormControl(props = {}) {
2192
2287
  fieldValues = cloneObject(values);
2193
2288
  }
2194
2289
  else {
2195
- await executeBuiltInValidation(_fields);
2290
+ await executeBuiltInValidation({
2291
+ fields: _fields,
2292
+ eventType: EVENTS.SUBMIT,
2293
+ });
2196
2294
  }
2197
2295
  if (_names.disabled.size) {
2198
2296
  for (const name of _names.disabled) {
2199
2297
  unset(fieldValues, name);
2200
2298
  }
2201
2299
  }
2202
- unset(_formState.errors, 'root');
2300
+ unset(_formState.errors, ROOT_ERROR_TYPE);
2203
2301
  if (isEmptyObject(_formState.errors)) {
2204
2302
  _subjects.state.next({
2205
2303
  errors: {},
@@ -2323,6 +2421,7 @@ function createFormControl(props = {}) {
2323
2421
  mount: keepStateOptions.keepDirtyValues ? _names.mount : new Set(),
2324
2422
  unMount: new Set(),
2325
2423
  array: new Set(),
2424
+ registerName: new Set(),
2326
2425
  disabled: new Set(),
2327
2426
  watch: new Set(),
2328
2427
  watchAll: false,