react-hook-form 7.78.0 → 7.80.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.
@@ -76,15 +76,16 @@ const INPUT_VALIDATION_RULES = {
76
76
  required: 'required',
77
77
  validate: 'validate',
78
78
  };
79
- const FORM_ERROR_TYPE = 'form';
80
79
  const ROOT_ERROR_TYPE = 'root';
81
80
  const PROTOTYPE_KEYWORDS = ['__proto__', 'constructor', 'prototype'];
82
81
 
83
- var isKey = (value) => /^\w*$/.test(value);
82
+ const IS_KEY_RE = /^\w*$/;
83
+ var isKey = (value) => IS_KEY_RE.test(value);
84
84
 
85
85
  var isUndefined = (val) => val === undefined;
86
86
 
87
- var stringToPath = (input) => input.split(/[.[\]'"]/g).filter(Boolean);
87
+ const FIELD_PATH_RE = /[.[\]'"]/;
88
+ var stringToPath = (input) => input.split(FIELD_PATH_RE).filter(Boolean);
88
89
 
89
90
  var get = (object, path, defaultValue) => {
90
91
  if (!path || !isObject(object)) {
@@ -249,7 +250,7 @@ var generateWatchOutput = (names, _names, formValues, isGlobal, defaultValue) =>
249
250
  var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
250
251
 
251
252
  const isEmptyObjectWithCustomPrototype = (object, keys) => keys.length === 0 && !Array.isArray(object) && !isPlainObject(object);
252
- function deepEqual(object1, object2, visited = new WeakSet()) {
253
+ function deepEqual(object1, object2, visited = new WeakMap()) {
253
254
  if (object1 === object2) {
254
255
  return true;
255
256
  }
@@ -268,11 +269,21 @@ function deepEqual(object1, object2, visited = new WeakSet()) {
268
269
  isEmptyObjectWithCustomPrototype(object2, keys2)) {
269
270
  return Object.is(object1, object2);
270
271
  }
271
- if (visited.has(object1) || visited.has(object2)) {
272
+ if (!keys1.length && Array.isArray(object1) !== Array.isArray(object2)) {
273
+ return false;
274
+ }
275
+ const visitedPairs = visited.get(object1);
276
+ if (visitedPairs && visitedPairs.has(object2)) {
272
277
  return true;
273
278
  }
274
- visited.add(object1);
275
- visited.add(object2);
279
+ if (visitedPairs) {
280
+ visitedPairs.add(object2);
281
+ }
282
+ else {
283
+ const ws = new WeakSet();
284
+ ws.add(object2);
285
+ visited.set(object1, ws);
286
+ }
276
287
  for (const key of keys1) {
277
288
  const val1 = object1[key];
278
289
  if (!(key in object2)) {
@@ -419,6 +430,7 @@ function useController(props) {
419
430
  exact,
420
431
  });
421
432
  const _props = React.useRef(props);
433
+ const _proxyRef = React.useRef(null);
422
434
  const _registerProps = React.useRef(control.register(name, {
423
435
  ...props.rules,
424
436
  value,
@@ -455,7 +467,7 @@ function useController(props) {
455
467
  value,
456
468
  });
457
469
  }
458
- _registerProps.current.onChange({
470
+ return _registerProps.current.onChange({
459
471
  target: {
460
472
  value: getEventValue(event),
461
473
  name: name,
@@ -471,15 +483,18 @@ function useController(props) {
471
483
  type: EVENTS.BLUR,
472
484
  }), [name, control._formValues]);
473
485
  const ref = React.useCallback((elm) => {
474
- const field = get(control._fields, name);
475
- if (field && field._f && elm) {
476
- field._f.ref = {
486
+ if (elm) {
487
+ _proxyRef.current = {
477
488
  focus: () => isFunction(elm.focus) && elm.focus(),
478
489
  select: () => isFunction(elm.select) && elm.select(),
479
490
  setCustomValidity: (message) => isFunction(elm.setCustomValidity) && elm.setCustomValidity(message),
480
491
  reportValidity: () => isFunction(elm.reportValidity) && elm.reportValidity(),
481
492
  };
482
493
  }
494
+ const field = get(control._fields, name);
495
+ if (field && field._f && elm) {
496
+ field._f.ref = _proxyRef.current;
497
+ }
483
498
  }, [control._fields, name]);
484
499
  const field = React.useMemo(() => ({
485
500
  name,
@@ -516,6 +531,12 @@ function useController(props) {
516
531
  }
517
532
  }
518
533
  !isArrayField && control.register(name);
534
+ if (_proxyRef.current) {
535
+ const field = get(control._fields, name);
536
+ if (field && field._f) {
537
+ field._f.ref = _proxyRef.current;
538
+ }
539
+ }
519
540
  return () => {
520
541
  (isArrayField
521
542
  ? _shouldUnregisterField && !control._state.action
@@ -1117,12 +1138,22 @@ var getValidationModes = (mode) => ({
1117
1138
  });
1118
1139
 
1119
1140
  const ASYNC_FUNCTION = 'AsyncFunction';
1120
- var hasPromiseValidation = (fieldReference) => !!fieldReference &&
1121
- !!fieldReference.validate &&
1122
- !!((isFunction(fieldReference.validate) &&
1123
- fieldReference.validate.constructor.name === ASYNC_FUNCTION) ||
1124
- (isObject(fieldReference.validate) &&
1125
- Object.values(fieldReference.validate).find((validateFunction) => validateFunction.constructor.name === ASYNC_FUNCTION)));
1141
+ var hasPromiseValidation = (fieldReference) => {
1142
+ if (!fieldReference || !fieldReference.validate)
1143
+ return false;
1144
+ if (isFunction(fieldReference.validate)) {
1145
+ return fieldReference.validate.constructor.name === ASYNC_FUNCTION;
1146
+ }
1147
+ if (isObject(fieldReference.validate)) {
1148
+ for (const key in fieldReference.validate) {
1149
+ if (fieldReference.validate[key].constructor
1150
+ .name === ASYNC_FUNCTION) {
1151
+ return true;
1152
+ }
1153
+ }
1154
+ }
1155
+ return false;
1156
+ };
1126
1157
 
1127
1158
  var hasValidation = (options) => options.mount &&
1128
1159
  (options.required ||
@@ -1133,10 +1164,17 @@ var hasValidation = (options) => options.mount &&
1133
1164
  options.pattern ||
1134
1165
  options.validate);
1135
1166
 
1136
- var isWatched = (name, _names, isBlurEvent) => !isBlurEvent &&
1137
- (_names.watchAll ||
1138
- _names.watch.has(name) ||
1139
- [..._names.watch].some((watchName) => name.startsWith(`${watchName}.`)));
1167
+ var isWatched = (name, _names, isBlurEvent) => {
1168
+ if (isBlurEvent)
1169
+ return false;
1170
+ if (_names.watchAll || _names.watch.has(name))
1171
+ return true;
1172
+ for (const watchName of _names.watch) {
1173
+ if (name.startsWith(watchName) && name.charAt(watchName.length) === '.')
1174
+ return true;
1175
+ }
1176
+ return false;
1177
+ };
1140
1178
 
1141
1179
  const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
1142
1180
  for (const key of fieldsNames || Object.keys(fields)) {
@@ -1204,10 +1242,10 @@ function schemaErrorLookup(errors, _fields, name) {
1204
1242
  var shouldRenderFormState = (formStateData, _proxyFormState, updateFormState, isRoot) => {
1205
1243
  updateFormState(formStateData);
1206
1244
  const { name, ...formState } = formStateData;
1207
- return (isEmptyObject(formState) ||
1208
- (isRoot &&
1209
- Object.keys(formState).length >= Object.keys(_proxyFormState).length) ||
1210
- Object.keys(formState).find((key) => _proxyFormState[key] ===
1245
+ const keys = Object.keys(formState);
1246
+ return (!keys.length ||
1247
+ (isRoot && keys.length >= Object.keys(_proxyFormState).length) ||
1248
+ keys.find((key) => _proxyFormState[key] ===
1211
1249
  (!isRoot || VALIDATION_MODE.all)));
1212
1250
  };
1213
1251
 
@@ -1274,7 +1312,13 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
1274
1312
  const inputRef = refs ? refs[0] : ref;
1275
1313
  const setCustomValidity = (message) => {
1276
1314
  if (shouldUseNativeValidation && inputRef.reportValidity) {
1277
- inputRef.setCustomValidity(isBoolean(message) ? '' : message || '');
1315
+ const validityMessage = isBoolean(message) ? '' : message || '';
1316
+ if (refs) {
1317
+ refs.forEach((ref) => ref.setCustomValidity(validityMessage));
1318
+ }
1319
+ else {
1320
+ inputRef.setCustomValidity(validityMessage);
1321
+ }
1278
1322
  inputRef.reportValidity();
1279
1323
  }
1280
1324
  };
@@ -1449,6 +1493,7 @@ const defaultOptions = {
1449
1493
  reValidateMode: VALIDATION_MODE.onChange,
1450
1494
  shouldFocusError: true,
1451
1495
  };
1496
+ const FORM_ERROR_TYPE = 'form';
1452
1497
  const DEFAULT_FORM_STATE = {
1453
1498
  submitCount: 0,
1454
1499
  isDirty: false,
@@ -1496,6 +1541,9 @@ function createFormControl(props = {}) {
1496
1541
  };
1497
1542
  let delayErrorCallback;
1498
1543
  let timer = 0;
1544
+ let _valuesSubscriberCount = 0;
1545
+ let _validationModeBeforeSubmit = getValidationModes(_options.mode);
1546
+ let _validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
1499
1547
  const defaultProxyFormState = {
1500
1548
  isDirty: false,
1501
1549
  dirtyFields: false,
@@ -1606,6 +1654,7 @@ function createFormControl(props = {}) {
1606
1654
  };
1607
1655
  const updateErrors = (name, error) => {
1608
1656
  set(_formState.errors, name, error);
1657
+ _formState.errors = { ..._formState.errors };
1609
1658
  _subjects.state.next({
1610
1659
  errors: _formState.errors,
1611
1660
  });
@@ -1648,12 +1697,6 @@ function createFormControl(props = {}) {
1648
1697
  : setFieldValue(name, defaultValue);
1649
1698
  if (_state.mount && !_state.action) {
1650
1699
  _setValid();
1651
- // Re-registering a field after a prior unregister puts its key back
1652
- // into _formValues, which can flip isDirty back to false (#13397).
1653
- // Only run when we are currently dirty, otherwise an initial register
1654
- // for a field with no defaultValue would flip isDirty to true. Reset
1655
- // paths repopulate _formValues before re-register, so the key is
1656
- // present then and this branch is skipped (preserves keepDirty).
1657
1700
  if (wasUnsetInFormValues &&
1658
1701
  _formState.isDirty &&
1659
1702
  (_proxyFormState.isDirty || _proxySubscribeFormState.isDirty)) {
@@ -1663,6 +1706,12 @@ function createFormControl(props = {}) {
1663
1706
  _subjects.state.next({ ..._formState });
1664
1707
  }
1665
1708
  }
1709
+ if (props.shouldUnregister &&
1710
+ wasUnsetInFormValues &&
1711
+ !isUndefined(get(_formValues, name)) &&
1712
+ isWatched(name, _names)) {
1713
+ _state.watch = true;
1714
+ }
1666
1715
  }
1667
1716
  }
1668
1717
  };
@@ -1674,12 +1723,13 @@ function createFormControl(props = {}) {
1674
1723
  };
1675
1724
  if (!_options.disabled) {
1676
1725
  if (!isBlurEvent || shouldDirty) {
1726
+ const isCurrentFieldPristine = deepEqual(get(_defaultValues, name), fieldValue);
1677
1727
  if (_proxyFormState.isDirty || _proxySubscribeFormState.isDirty) {
1678
1728
  isPreviousDirty = _formState.isDirty;
1679
- _formState.isDirty = output.isDirty = _getDirty();
1729
+ _formState.isDirty = output.isDirty =
1730
+ !isCurrentFieldPristine || _getDirty();
1680
1731
  shouldUpdateField = isPreviousDirty !== output.isDirty;
1681
1732
  }
1682
- const isCurrentFieldPristine = deepEqual(get(_defaultValues, name), fieldValue);
1683
1733
  isPreviousDirty = !!get(_formState.dirtyFields, name);
1684
1734
  if (isCurrentFieldPristine !== _formState.isDirty) {
1685
1735
  _formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
@@ -1727,6 +1777,7 @@ function createFormControl(props = {}) {
1727
1777
  error
1728
1778
  ? set(_formState.errors, name, error)
1729
1779
  : unset(_formState.errors, name);
1780
+ _formState.errors = { ..._formState.errors };
1730
1781
  }
1731
1782
  if ((error ? !deepEqual(previousFieldError, error) : previousFieldError) ||
1732
1783
  !isEmptyObject(fieldState) ||
@@ -1762,6 +1813,7 @@ function createFormControl(props = {}) {
1762
1813
  : set(_formState.errors, name, error)
1763
1814
  : unset(_formState.errors, name);
1764
1815
  }
1816
+ _formState.errors = { ..._formState.errors };
1765
1817
  }
1766
1818
  else {
1767
1819
  _formState.errors = errors;
@@ -1876,7 +1928,7 @@ function createFormControl(props = {}) {
1876
1928
  };
1877
1929
  const _getDirty = (name, data) => !_options.disabled &&
1878
1930
  (name && data && set(_formValues, name, data),
1879
- !deepEqual(getValues(), _defaultValues));
1931
+ !deepEqual(_state.mount ? _formValues : _defaultValues, _defaultValues));
1880
1932
  const _getWatch = (names, defaultValue, isGlobal) => generateWatchOutput(names, _names, {
1881
1933
  ...(_state.mount
1882
1934
  ? _formValues
@@ -1887,7 +1939,7 @@ function createFormControl(props = {}) {
1887
1939
  : defaultValue),
1888
1940
  }, isGlobal, defaultValue);
1889
1941
  const _getFieldArray = (name) => compact(get(_state.mount ? _formValues : _defaultValues, name, _options.shouldUnregister ? get(_defaultValues, name, []) : []));
1890
- const setFieldValue = (name, value, options = {}, skipClone = false) => {
1942
+ const setFieldValue = (name, value, options = {}, skipClone = false, skipRender = false) => {
1891
1943
  const field = get(_fields, name);
1892
1944
  let fieldValue = value;
1893
1945
  if (field) {
@@ -1925,7 +1977,7 @@ function createFormControl(props = {}) {
1925
1977
  }
1926
1978
  else {
1927
1979
  fieldReference.ref.value = fieldValue;
1928
- if (!fieldReference.ref.type) {
1980
+ if (!fieldReference.ref.type && !skipRender) {
1929
1981
  _subjects.state.next({
1930
1982
  name,
1931
1983
  values: skipClone ? _formValues : cloneObject(_formValues),
@@ -1935,10 +1987,10 @@ function createFormControl(props = {}) {
1935
1987
  }
1936
1988
  }
1937
1989
  (options.shouldDirty || options.shouldTouch) &&
1938
- updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty, true);
1990
+ updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty, !skipRender);
1939
1991
  options.shouldValidate && trigger(name);
1940
1992
  };
1941
- const setFieldValues = (name, value, options, skipClone = false) => {
1993
+ const setFieldValues = (name, value, options, skipClone = false, skipRender = false) => {
1942
1994
  for (const fieldKey in value) {
1943
1995
  if (!value.hasOwnProperty(fieldKey)) {
1944
1996
  return;
@@ -1950,11 +2002,11 @@ function createFormControl(props = {}) {
1950
2002
  isObject(fieldValue) ||
1951
2003
  (field && !field._f)) &&
1952
2004
  !isDateObject(fieldValue)
1953
- ? setFieldValues(fieldName, fieldValue, options, skipClone)
1954
- : setFieldValue(fieldName, fieldValue, options, skipClone);
2005
+ ? setFieldValues(fieldName, fieldValue, options, skipClone, skipRender)
2006
+ : setFieldValue(fieldName, fieldValue, options, skipClone, skipRender);
1955
2007
  }
1956
2008
  };
1957
- const _setValue = (name, value, options, skipClone) => {
2009
+ const _setValue = (name, value, options, skipClone, skipStateEmit = false) => {
1958
2010
  const field = get(_fields, name);
1959
2011
  const isFieldArray = _names.array.has(name);
1960
2012
  const cloneValue = skipClone ? value : cloneObject(value);
@@ -1974,24 +2026,26 @@ function createFormControl(props = {}) {
1974
2026
  _proxySubscribeFormState.dirtyFields) &&
1975
2027
  options.shouldDirty) {
1976
2028
  _updateDirtyFields();
1977
- _subjects.state.next({
1978
- name,
1979
- dirtyFields: _formState.dirtyFields,
1980
- isDirty: _getDirty(name, cloneValue),
1981
- });
2029
+ if (!skipStateEmit) {
2030
+ _subjects.state.next({
2031
+ name,
2032
+ dirtyFields: _formState.dirtyFields,
2033
+ isDirty: _getDirty(name, cloneValue),
2034
+ });
2035
+ }
1982
2036
  }
1983
2037
  }
1984
2038
  else {
1985
2039
  const isEmpty = (Array.isArray(cloneValue) && !cloneValue.length) ||
1986
2040
  isEmptyObject(cloneValue);
1987
2041
  if (!field || field._f || isNullOrUndefined(cloneValue) || isEmpty) {
1988
- setFieldValue(name, cloneValue, options, skipClone);
2042
+ setFieldValue(name, cloneValue, options, skipClone, skipStateEmit);
1989
2043
  }
1990
2044
  else {
1991
- setFieldValues(name, cloneValue, options, skipClone);
2045
+ setFieldValues(name, cloneValue, options, skipClone, skipStateEmit);
1992
2046
  }
1993
2047
  }
1994
- if (!isValueUnchanged) {
2048
+ if (!isValueUnchanged && !skipStateEmit) {
1995
2049
  const watched = isWatched(name, _names);
1996
2050
  const values = skipClone ? _formValues : cloneObject(_formValues);
1997
2051
  _subjects.state.next({
@@ -2012,13 +2066,13 @@ function createFormControl(props = {}) {
2012
2066
  ...updatedFormValues,
2013
2067
  };
2014
2068
  for (const fieldName of _names.mount) {
2015
- _setValue(fieldName, get(updatedFormValues, fieldName), options, true);
2069
+ _setValue(fieldName, get(updatedFormValues, fieldName), options, true, true);
2016
2070
  }
2017
2071
  _subjects.state.next({
2018
2072
  ..._formState,
2019
2073
  name: undefined,
2020
2074
  type: undefined,
2021
- values: _formValues,
2075
+ ...(_valuesSubscriberCount ? { values: _formValues } : {}),
2022
2076
  });
2023
2077
  if (options.shouldValidate) {
2024
2078
  _setValid();
@@ -2037,8 +2091,6 @@ function createFormControl(props = {}) {
2037
2091
  (isDateObject(fieldValue) && isNaN(fieldValue.getTime())) ||
2038
2092
  deepEqual(fieldValue, get(_formValues, name, fieldValue));
2039
2093
  };
2040
- const validationModeBeforeSubmit = getValidationModes(_options.mode);
2041
- const validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
2042
2094
  if (field) {
2043
2095
  let error;
2044
2096
  let isValid;
@@ -2046,12 +2098,13 @@ function createFormControl(props = {}) {
2046
2098
  ? getFieldValue(field._f)
2047
2099
  : getEventValue(event);
2048
2100
  const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
2049
- const shouldSkipValidation = (!hasValidation(field._f) &&
2101
+ const hasNoValidationEffect = !hasValidation(field._f) &&
2050
2102
  !props.validate &&
2051
2103
  !_options.resolver &&
2052
2104
  !get(_formState.errors, name) &&
2053
- !field._f.deps) ||
2054
- skipValidation(isBlurEvent, get(_formState.touchedFields, name), _formState.isSubmitted, validationModeAfterSubmit, validationModeBeforeSubmit);
2105
+ !field._f.deps;
2106
+ const shouldSkipValidation = hasNoValidationEffect ||
2107
+ skipValidation(isBlurEvent, get(_formState.touchedFields, name), _formState.isSubmitted, _validationModeAfterSubmit, _validationModeBeforeSubmit);
2055
2108
  const watched = isWatched(name, _names, isBlurEvent);
2056
2109
  set(_formValues, name, fieldValue);
2057
2110
  if (isBlurEvent) {
@@ -2069,10 +2122,13 @@ function createFormControl(props = {}) {
2069
2122
  _subjects.state.next({
2070
2123
  name,
2071
2124
  type: event.type,
2072
- values: cloneObject(_formValues),
2125
+ ...(_valuesSubscriberCount
2126
+ ? { values: cloneObject(_formValues) }
2127
+ : {}),
2073
2128
  });
2074
2129
  if (shouldSkipValidation) {
2075
- if (_proxyFormState.isValid || _proxySubscribeFormState.isValid) {
2130
+ if ((!hasNoValidationEffect || !_formState.isValid) &&
2131
+ (_proxyFormState.isValid || _proxySubscribeFormState.isValid)) {
2076
2132
  if (_options.mode === 'onBlur') {
2077
2133
  if (isBlurEvent) {
2078
2134
  _setValid();
@@ -2207,8 +2263,6 @@ function createFormControl(props = {}) {
2207
2263
  const names = name ? convertToArrayPayload(name) : undefined;
2208
2264
  names === null || names === void 0 ? void 0 : names.forEach((inputName) => unset(_formState.errors, inputName));
2209
2265
  if (names) {
2210
- // Emit for each cleared field with the field name so that
2211
- // shouldSubscribeByName can filter and avoid broad re-renders
2212
2266
  names.forEach((inputName) => {
2213
2267
  _subjects.state.next({
2214
2268
  name: inputName,
@@ -2217,7 +2271,6 @@ function createFormControl(props = {}) {
2217
2271
  });
2218
2272
  }
2219
2273
  else {
2220
- // Clear all errors - emit without name to notify all subscribers
2221
2274
  _subjects.state.next({
2222
2275
  errors: {},
2223
2276
  });
@@ -2226,7 +2279,6 @@ function createFormControl(props = {}) {
2226
2279
  const setError = (name, error, options) => {
2227
2280
  const ref = (get(_fields, name, { _f: {} })._f || {}).ref;
2228
2281
  const currentError = get(_formState.errors, name) || {};
2229
- // Don't override existing error messages elsewhere in the object tree.
2230
2282
  const { ref: currentRef, message, type, ...restOfErrorTree } = currentError;
2231
2283
  set(_formState.errors, name, {
2232
2284
  ...restOfErrorTree,
@@ -2240,26 +2292,60 @@ function createFormControl(props = {}) {
2240
2292
  });
2241
2293
  options && options.shouldFocus && ref && ref.focus && ref.focus();
2242
2294
  };
2243
- const watch = (name, defaultValue) => isFunction(name)
2244
- ? _subjects.state.subscribe({
2245
- next: (payload) => 'values' in payload &&
2246
- name(payload.values || _getWatch(undefined, defaultValue), payload),
2247
- })
2248
- : _getWatch(name, defaultValue, true);
2249
- const _subscribe = (props) => _subjects.state.subscribe({
2250
- next: (formState) => {
2251
- if (shouldSubscribeByName(props.name, formState.name, props.exact) &&
2252
- shouldRenderFormState(formState, props.formState || _proxyFormState, _setFormState, props.reRenderRoot)) {
2253
- const snapshot = { ..._formValues };
2254
- props.callback({
2255
- values: snapshot,
2256
- ..._formState,
2257
- ...formState,
2258
- defaultValues: _defaultValues,
2259
- });
2295
+ const watch = (name, defaultValue) => {
2296
+ if (isFunction(name)) {
2297
+ _valuesSubscriberCount++;
2298
+ const { unsubscribe } = _subjects.state.subscribe({
2299
+ next: (payload) => 'values' in payload &&
2300
+ name(payload.values || _getWatch(undefined, defaultValue), payload),
2301
+ });
2302
+ let called = false;
2303
+ return {
2304
+ unsubscribe: () => {
2305
+ if (called) {
2306
+ return;
2307
+ }
2308
+ called = true;
2309
+ _valuesSubscriberCount--;
2310
+ unsubscribe();
2311
+ },
2312
+ };
2313
+ }
2314
+ return _getWatch(name, defaultValue, true);
2315
+ };
2316
+ const _subscribe = (props) => {
2317
+ var _a;
2318
+ const needsValues = !!((_a = props.formState) === null || _a === void 0 ? void 0 : _a.values);
2319
+ if (needsValues) {
2320
+ _valuesSubscriberCount++;
2321
+ }
2322
+ const { unsubscribe } = _subjects.state.subscribe({
2323
+ next: (formState) => {
2324
+ if (shouldSubscribeByName(props.name, formState.name, props.exact) &&
2325
+ shouldRenderFormState(formState, props.formState || _proxyFormState, _setFormState, props.reRenderRoot)) {
2326
+ const snapshot = { ..._formValues };
2327
+ props.callback({
2328
+ values: snapshot,
2329
+ ..._formState,
2330
+ ...formState,
2331
+ defaultValues: _defaultValues,
2332
+ });
2333
+ }
2334
+ },
2335
+ });
2336
+ if (!needsValues) {
2337
+ return unsubscribe;
2338
+ }
2339
+ let called = false;
2340
+ return () => {
2341
+ if (called) {
2342
+ return;
2260
2343
  }
2261
- },
2262
- }).unsubscribe;
2344
+ called = true;
2345
+ _valuesSubscriberCount--;
2346
+ unsubscribe();
2347
+ };
2348
+ };
2263
2349
  const subscribe = (props) => {
2264
2350
  _state.mount = true;
2265
2351
  _proxySubscribeFormState = {
@@ -2593,9 +2679,6 @@ function createFormControl(props = {}) {
2593
2679
  _state.watch = !!_options.shouldUnregister;
2594
2680
  _state.keepIsValid = !!keepStateOptions.keepIsValid;
2595
2681
  _state.action = false;
2596
- // Clear errors synchronously to prevent validation errors on subsequent submissions
2597
- // This fixes the issue where form.reset() causes validation errors on subsequent
2598
- // submissions in Next.js 16 with Server Actions
2599
2682
  if (!keepStateOptions.keepErrors) {
2600
2683
  _formState.errors = {};
2601
2684
  }
@@ -2647,8 +2730,6 @@ function createFormControl(props = {}) {
2647
2730
  ? fieldReference.refs[0]
2648
2731
  : fieldReference.ref;
2649
2732
  if (fieldRef.focus) {
2650
- // Use setTimeout to ensure focus happens after any pending state updates
2651
- // This fixes the issue where setFocus doesn't work immediately after setError
2652
2733
  setTimeout(() => {
2653
2734
  fieldRef.focus();
2654
2735
  options.shouldSelect &&
@@ -2742,6 +2823,8 @@ function createFormControl(props = {}) {
2742
2823
  ..._options,
2743
2824
  ...value,
2744
2825
  };
2826
+ _validationModeBeforeSubmit = getValidationModes(_options.mode);
2827
+ _validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
2745
2828
  },
2746
2829
  },
2747
2830
  subscribe,
@@ -2875,34 +2958,45 @@ var updateAt = (fieldValues, index, value) => {
2875
2958
  */
2876
2959
  function useFieldArray(props) {
2877
2960
  const formControl = useFormControlContext();
2878
- const { control = formControl, name, keyName = 'id', shouldUnregister, rules, } = props;
2961
+ const { control = formControl, name, keyName = 'id', disabled, shouldUnregister, rules, } = props;
2879
2962
  const [fields, setFields] = React.useState(control._getFieldArray(name));
2880
2963
  const ids = React.useRef(control._getFieldArray(name).map(generateId));
2881
2964
  const _actioned = React.useRef(false);
2882
- control._names.array.add(name);
2883
- React.useMemo(() => rules &&
2965
+ if (!disabled) {
2966
+ control._names.array.add(name);
2967
+ }
2968
+ React.useMemo(() => !disabled &&
2969
+ rules &&
2884
2970
  fields.length >= 0 &&
2885
- control.register(name, rules), [control, name, fields.length, rules]);
2886
- useIsomorphicLayoutEffect(() => control._subjects.array.subscribe({
2887
- next: ({ values, name: fieldArrayName, }) => {
2888
- if (fieldArrayName === name || !fieldArrayName) {
2889
- const fieldValues = get(values, name);
2890
- if (Array.isArray(fieldValues)) {
2891
- setFields(fieldValues);
2892
- ids.current = fieldValues.map(generateId);
2893
- }
2894
- else if (!fieldArrayName) {
2895
- setFields([]);
2896
- ids.current = [];
2971
+ control.register(name, rules), [control, name, fields.length, rules, disabled]);
2972
+ useIsomorphicLayoutEffect(() => {
2973
+ if (disabled) {
2974
+ return;
2975
+ }
2976
+ return control._subjects.array.subscribe({
2977
+ next: ({ values, name: fieldArrayName, }) => {
2978
+ if (fieldArrayName === name || !fieldArrayName) {
2979
+ const fieldValues = get(values, name);
2980
+ if (Array.isArray(fieldValues)) {
2981
+ setFields(fieldValues);
2982
+ ids.current = fieldValues.map(generateId);
2983
+ }
2984
+ else if (!fieldArrayName) {
2985
+ setFields([]);
2986
+ ids.current = [];
2987
+ }
2897
2988
  }
2898
- }
2899
- },
2900
- }).unsubscribe, [control, name]);
2989
+ },
2990
+ }).unsubscribe;
2991
+ }, [control, name, disabled]);
2901
2992
  const updateValues = React.useCallback((updatedFieldArrayValues) => {
2902
2993
  _actioned.current = true;
2903
2994
  control._setFieldArray(name, updatedFieldArrayValues);
2904
2995
  }, [control, name]);
2905
2996
  const append = (value, options) => {
2997
+ if (disabled) {
2998
+ return;
2999
+ }
2906
3000
  const appendValue = convertToArrayPayload(cloneObject(value));
2907
3001
  const updatedFieldArrayValues = appendAt(control._getFieldArray(name), appendValue);
2908
3002
  control._names.focus = getFocusFieldName(name, updatedFieldArrayValues.length - 1, options);
@@ -2914,6 +3008,9 @@ function useFieldArray(props) {
2914
3008
  });
2915
3009
  };
2916
3010
  const prepend = (value, options) => {
3011
+ if (disabled) {
3012
+ return;
3013
+ }
2917
3014
  const prependValue = convertToArrayPayload(cloneObject(value));
2918
3015
  const updatedFieldArrayValues = prependAt(control._getFieldArray(name), prependValue);
2919
3016
  control._names.focus = getFocusFieldName(name, 0, options);
@@ -2925,6 +3022,9 @@ function useFieldArray(props) {
2925
3022
  });
2926
3023
  };
2927
3024
  const remove = (index) => {
3025
+ if (disabled) {
3026
+ return;
3027
+ }
2928
3028
  const updatedFieldArrayValues = removeArrayAt(control._getFieldArray(name), index);
2929
3029
  ids.current = removeArrayAt(ids.current, index);
2930
3030
  updateValues(updatedFieldArrayValues);
@@ -2936,6 +3036,9 @@ function useFieldArray(props) {
2936
3036
  });
2937
3037
  };
2938
3038
  const insert$1 = (index, value, options) => {
3039
+ if (disabled) {
3040
+ return;
3041
+ }
2939
3042
  const insertValue = convertToArrayPayload(cloneObject(value));
2940
3043
  const updatedFieldArrayValues = insert(control._getFieldArray(name), index, insertValue);
2941
3044
  control._names.focus = getFocusFieldName(name, index, options);
@@ -2948,6 +3051,9 @@ function useFieldArray(props) {
2948
3051
  });
2949
3052
  };
2950
3053
  const swap = (indexA, indexB) => {
3054
+ if (disabled) {
3055
+ return;
3056
+ }
2951
3057
  const updatedFieldArrayValues = control._getFieldArray(name);
2952
3058
  swapArrayAt(updatedFieldArrayValues, indexA, indexB);
2953
3059
  swapArrayAt(ids.current, indexA, indexB);
@@ -2959,6 +3065,9 @@ function useFieldArray(props) {
2959
3065
  }, false);
2960
3066
  };
2961
3067
  const move = (from, to) => {
3068
+ if (disabled) {
3069
+ return;
3070
+ }
2962
3071
  const updatedFieldArrayValues = control._getFieldArray(name);
2963
3072
  moveArrayAt(updatedFieldArrayValues, from, to);
2964
3073
  moveArrayAt(ids.current, from, to);
@@ -2970,6 +3079,9 @@ function useFieldArray(props) {
2970
3079
  }, false);
2971
3080
  };
2972
3081
  const update = (index, value) => {
3082
+ if (disabled) {
3083
+ return;
3084
+ }
2973
3085
  const updateValue = cloneObject(value);
2974
3086
  const updatedFieldArrayValues = updateAt(control._getFieldArray(name), index, updateValue);
2975
3087
  ids.current = [...updatedFieldArrayValues].map((item, i) => !item || i === index ? generateId() : ids.current[i]);
@@ -2981,6 +3093,9 @@ function useFieldArray(props) {
2981
3093
  }, true, false);
2982
3094
  };
2983
3095
  const replace = (value) => {
3096
+ if (disabled) {
3097
+ return;
3098
+ }
2984
3099
  const updatedFieldArrayValues = convertToArrayPayload(cloneObject(value));
2985
3100
  ids.current = updatedFieldArrayValues.map(generateId);
2986
3101
  updateValues([...updatedFieldArrayValues]);
@@ -2988,6 +3103,9 @@ function useFieldArray(props) {
2988
3103
  control._setFieldArray(name, [...updatedFieldArrayValues], (data) => data, {}, true, false);
2989
3104
  };
2990
3105
  React.useEffect(() => {
3106
+ if (disabled) {
3107
+ return;
3108
+ }
2991
3109
  control._state.action = false;
2992
3110
  isWatched(name, control._names) &&
2993
3111
  control._subjects.state.next({
@@ -3048,10 +3166,15 @@ function useFieldArray(props) {
3048
3166
  control._names.focus = '';
3049
3167
  control._setValid();
3050
3168
  _actioned.current = false;
3051
- }, [fields, name, control]);
3169
+ }, [fields, name, control, disabled]);
3052
3170
  React.useEffect(() => {
3053
- !get(control._formValues, name) && control._setFieldArray(name);
3171
+ if (!disabled) {
3172
+ !get(control._formValues, name) && control._setFieldArray(name);
3173
+ }
3054
3174
  return () => {
3175
+ if (disabled) {
3176
+ return;
3177
+ }
3055
3178
  const shouldKeepFieldArrayValues = !(control._options.shouldUnregister || shouldUnregister);
3056
3179
  const updateMounted = (name, value) => {
3057
3180
  const field = get(control._fields, name);
@@ -3069,20 +3192,31 @@ function useFieldArray(props) {
3069
3192
  ? updateMounted(name, false)
3070
3193
  : control.unregister(name);
3071
3194
  };
3072
- }, [name, control, keyName, shouldUnregister]);
3195
+ }, [name, control, keyName, shouldUnregister, disabled]);
3073
3196
  return {
3074
- swap: React.useCallback(swap, [updateValues, name, control]),
3075
- move: React.useCallback(move, [updateValues, name, control]),
3076
- prepend: React.useCallback(prepend, [updateValues, name, control]),
3077
- append: React.useCallback(append, [updateValues, name, control]),
3078
- remove: React.useCallback(remove, [updateValues, name, control]),
3079
- insert: React.useCallback(insert$1, [updateValues, name, control]),
3080
- update: React.useCallback(update, [updateValues, name, control]),
3081
- replace: React.useCallback(replace, [updateValues, name, control]),
3197
+ swap: React.useCallback(swap, [updateValues, name, control, disabled]),
3198
+ move: React.useCallback(move, [updateValues, name, control, disabled]),
3199
+ prepend: React.useCallback(prepend, [
3200
+ updateValues,
3201
+ name,
3202
+ control,
3203
+ disabled,
3204
+ ]),
3205
+ append: React.useCallback(append, [updateValues, name, control, disabled]),
3206
+ remove: React.useCallback(remove, [updateValues, name, control, disabled]),
3207
+ insert: React.useCallback(insert$1, [updateValues, name, control, disabled]),
3208
+ update: React.useCallback(update, [updateValues, name, control, disabled]),
3209
+ replace: React.useCallback(replace, [
3210
+ updateValues,
3211
+ name,
3212
+ control,
3213
+ disabled,
3214
+ ]),
3082
3215
  fields: React.useMemo(() => fields.map((field, index) => ({
3083
3216
  ...field,
3217
+ ...(isBoolean(disabled) ? { disabled } : {}),
3084
3218
  [keyName]: ids.current[index] || generateId(),
3085
- })), [fields, keyName]),
3219
+ })), [fields, keyName, disabled]),
3086
3220
  };
3087
3221
  }
3088
3222
 
@@ -3118,6 +3252,7 @@ function useFieldArray(props) {
3118
3252
  function useForm(props = {}) {
3119
3253
  const _formControl = React.useRef(undefined);
3120
3254
  const _values = React.useRef(undefined);
3255
+ const _formControlProp = React.useRef(props.formControl);
3121
3256
  const [formState, updateFormState] = React.useState(() => ({
3122
3257
  ...cloneObject(DEFAULT_FORM_STATE),
3123
3258
  isLoading: isFunction(props.defaultValues),
@@ -3127,7 +3262,9 @@ function useForm(props = {}) {
3127
3262
  ? undefined
3128
3263
  : props.defaultValues,
3129
3264
  }));
3130
- if (!_formControl.current) {
3265
+ if (!_formControl.current ||
3266
+ (props.formControl && _formControlProp.current !== props.formControl)) {
3267
+ _formControlProp.current = props.formControl;
3131
3268
  if (props.formControl) {
3132
3269
  _formControl.current = {
3133
3270
  ...props.formControl,