@depup/react-hook-form 7.84.0-depup.0 → 7.86.0-depup.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 (58) hide show
  1. package/README.md +2 -2
  2. package/changes.json +1 -1
  3. package/dist/controller.d.ts +1 -2
  4. package/dist/controller.d.ts.map +1 -1
  5. package/dist/form.d.ts +1 -2
  6. package/dist/form.d.ts.map +1 -1
  7. package/dist/{formStateSubscribe.d.ts → formState.d.ts} +1 -1
  8. package/dist/formState.d.ts.map +1 -0
  9. package/dist/index.cjs.js +1 -1
  10. package/dist/index.cjs.js.map +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.esm.mjs +323 -170
  14. package/dist/index.esm.mjs.map +1 -1
  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/generateId.d.ts.map +1 -1
  19. package/dist/logic/getCheckboxValue.d.ts.map +1 -1
  20. package/dist/logic/getFieldArrayItemNames.d.ts +4 -0
  21. package/dist/logic/getFieldArrayItemNames.d.ts.map +1 -0
  22. package/dist/logic/getProxyFormState.d.ts +1 -1
  23. package/dist/logic/getProxyFormState.d.ts.map +1 -1
  24. package/dist/logic/hasValidation.d.ts +1 -1
  25. package/dist/logic/hasValidation.d.ts.map +1 -1
  26. package/dist/logic/unsetEmptyArray.d.ts +1 -1
  27. package/dist/logic/unsetEmptyArray.d.ts.map +1 -1
  28. package/dist/logic/validateField.d.ts.map +1 -1
  29. package/dist/types/errors.d.ts +6 -1
  30. package/dist/types/errors.d.ts.map +1 -1
  31. package/dist/types/form.d.ts +49 -3
  32. package/dist/types/form.d.ts.map +1 -1
  33. package/dist/useFieldArray.d.ts.map +1 -1
  34. package/dist/useForm.d.ts.map +1 -1
  35. package/dist/useFormContext.d.ts +1 -1
  36. package/dist/useFormContext.d.ts.map +1 -1
  37. package/dist/useFormState.d.ts.map +1 -1
  38. package/dist/useResyncOnReconnect.d.ts +5 -0
  39. package/dist/useResyncOnReconnect.d.ts.map +1 -0
  40. package/dist/useWatch.d.ts.map +1 -1
  41. package/dist/utils/cloneObject.d.ts.map +1 -1
  42. package/dist/utils/deepEqual.d.ts +1 -1
  43. package/dist/utils/deepEqual.d.ts.map +1 -1
  44. package/dist/utils/flatten.d.ts.map +1 -1
  45. package/dist/utils/has.d.ts +3 -0
  46. package/dist/utils/has.d.ts.map +1 -0
  47. package/dist/utils/json.d.ts +2 -2
  48. package/dist/utils/json.d.ts.map +1 -1
  49. package/package.json +4 -8
  50. package/dist/formStateSubscribe.d.ts.map +0 -1
  51. package/dist/index.react-server.d.ts +0 -3
  52. package/dist/index.react-server.d.ts.map +0 -1
  53. package/dist/logic/getNodeParentName.d.ts +0 -3
  54. package/dist/logic/getNodeParentName.d.ts.map +0 -1
  55. package/dist/react-server.esm.mjs +0 -2319
  56. package/dist/react-server.esm.mjs.map +0 -1
  57. package/dist/utils/deepMerge.d.ts +0 -2
  58. package/dist/utils/deepMerge.d.ts.map +0 -1
@@ -26,16 +26,14 @@ var isNameInFieldArray = (names, name) => name
26
26
  .split('.')
27
27
  .some((part, index, arr) => !isNaN(Number(part)) && names.has(arr.slice(0, index).join('.')));
28
28
 
29
- var isPlainObject = (tempObject) => {
30
- const prototypeCopy = tempObject.constructor && tempObject.constructor.prototype;
31
- return (isObject(prototypeCopy) && prototypeCopy.hasOwnProperty('isPrototypeOf'));
32
- };
33
-
34
29
  var isWeb = typeof window !== 'undefined' &&
35
30
  typeof window.HTMLElement !== 'undefined' &&
36
31
  typeof document !== 'undefined';
37
32
 
38
33
  function cloneObject(data) {
34
+ if (data === null || typeof data !== 'object') {
35
+ return data;
36
+ }
39
37
  if (data instanceof Date) {
40
38
  return new Date(data);
41
39
  }
@@ -44,7 +42,7 @@ function cloneObject(data) {
44
42
  return data;
45
43
  }
46
44
  const isArray = Array.isArray(data);
47
- if (!isArray && !(isObject(data) && isPlainObject(data))) {
45
+ if (!isArray && data.constructor !== Object) {
48
46
  return data;
49
47
  }
50
48
  const copy = isArray ? [] : Object.create(Object.getPrototypeOf(data));
@@ -170,6 +168,87 @@ const useIsomorphicLayoutEffect = isWeb
170
168
  ? React.useLayoutEffect
171
169
  : React.useEffect;
172
170
 
171
+ var isPlainObject = (tempObject) => {
172
+ const prototypeCopy = tempObject.constructor && tempObject.constructor.prototype;
173
+ return (isObject(prototypeCopy) && prototypeCopy.hasOwnProperty('isPrototypeOf'));
174
+ };
175
+
176
+ var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
177
+
178
+ const isEmptyObjectWithCustomPrototype = (object, keys) => keys.length === 0 && !Array.isArray(object) && !isPlainObject(object);
179
+ function deepEqual(object1, object2, visited = new WeakMap()) {
180
+ if (object1 === object2) {
181
+ return true;
182
+ }
183
+ if (isPrimitive(object1) || isPrimitive(object2)) {
184
+ return Object.is(object1, object2);
185
+ }
186
+ if (isDateObject(object1) && isDateObject(object2)) {
187
+ return Object.is(object1.getTime(), object2.getTime());
188
+ }
189
+ const keys1 = Object.keys(object1);
190
+ const keys2 = Object.keys(object2);
191
+ if (keys1.length !== keys2.length) {
192
+ return false;
193
+ }
194
+ if (isEmptyObjectWithCustomPrototype(object1, keys1) ||
195
+ isEmptyObjectWithCustomPrototype(object2, keys2)) {
196
+ return Object.is(object1, object2);
197
+ }
198
+ if (!keys1.length && Array.isArray(object1) !== Array.isArray(object2)) {
199
+ return false;
200
+ }
201
+ const visitedPairs = visited.get(object1);
202
+ if (visitedPairs && visitedPairs.has(object2)) {
203
+ return true;
204
+ }
205
+ if (visitedPairs) {
206
+ visitedPairs.add(object2);
207
+ }
208
+ else {
209
+ const ws = new WeakSet();
210
+ ws.add(object2);
211
+ visited.set(object1, ws);
212
+ }
213
+ for (const key of keys1) {
214
+ const val1 = object1[key];
215
+ if (!(key in object2)) {
216
+ return false;
217
+ }
218
+ if (key !== 'ref') {
219
+ const val2 = object2[key];
220
+ if ((isDateObject(val1) && isDateObject(val2)) ||
221
+ ((isObject(val1) || Array.isArray(val1)) &&
222
+ (isObject(val2) || Array.isArray(val2)))
223
+ ? !deepEqual(val1, val2, visited)
224
+ : !Object.is(val1, val2)) {
225
+ return false;
226
+ }
227
+ }
228
+ }
229
+ return true;
230
+ }
231
+
232
+ function useResyncOnReconnect() {
233
+ const _connected = React.useRef(false);
234
+ const _prevValue = React.useRef(undefined);
235
+ const resyncIfNeeded = React.useCallback((enabled, getCurrentValue, setValue) => {
236
+ if (enabled && _connected.current) {
237
+ const currentValue = getCurrentValue();
238
+ if (!deepEqual(_prevValue.current, currentValue)) {
239
+ setValue(currentValue);
240
+ }
241
+ }
242
+ _connected.current = true;
243
+ }, []);
244
+ const snapshot = React.useCallback((enabled, getCurrentValue) => {
245
+ if (enabled) {
246
+ _prevValue.current = cloneObject(getCurrentValue());
247
+ }
248
+ }, []);
249
+ return { resyncIfNeeded, snapshot };
250
+ }
251
+
173
252
  /**
174
253
  * 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.
175
254
  *
@@ -217,19 +296,31 @@ function useFormState(props) {
217
296
  isValid: false,
218
297
  errors: false,
219
298
  });
220
- useIsomorphicLayoutEffect(() => control._subscribe({
221
- name,
222
- formState: _localProxyFormState.current,
223
- exact,
224
- callback: (formState) => {
225
- !disabled &&
226
- updateFormState({
227
- ...control._formState,
228
- ...formState,
229
- defaultValues: control._defaultValues,
230
- });
231
- },
232
- }), [name, disabled, exact]);
299
+ const { resyncIfNeeded, snapshot } = useResyncOnReconnect();
300
+ useIsomorphicLayoutEffect(() => {
301
+ const getCurrentFormState = () => ({
302
+ ...control._formState,
303
+ defaultValues: control._defaultValues,
304
+ });
305
+ resyncIfNeeded(!disabled, getCurrentFormState, updateFormState);
306
+ const unsubscribe = control._subscribe({
307
+ name,
308
+ formState: _localProxyFormState.current,
309
+ exact,
310
+ callback: (formState) => {
311
+ !disabled &&
312
+ updateFormState({
313
+ ...control._formState,
314
+ ...formState,
315
+ defaultValues: control._defaultValues,
316
+ });
317
+ },
318
+ });
319
+ return () => {
320
+ unsubscribe();
321
+ snapshot(!disabled, getCurrentFormState);
322
+ };
323
+ }, [name, disabled, exact, resyncIfNeeded, snapshot]);
233
324
  React.useEffect(() => {
234
325
  _localProxyFormState.current.isValid && control._setValid(true);
235
326
  }, [control]);
@@ -251,62 +342,6 @@ var generateWatchOutput = (names, _names, formValues, isGlobal, defaultValue) =>
251
342
  return formValues;
252
343
  };
253
344
 
254
- var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
255
-
256
- const isEmptyObjectWithCustomPrototype = (object, keys) => keys.length === 0 && !Array.isArray(object) && !isPlainObject(object);
257
- function deepEqual(object1, object2, visited = new WeakMap()) {
258
- if (object1 === object2) {
259
- return true;
260
- }
261
- if (isPrimitive(object1) || isPrimitive(object2)) {
262
- return Object.is(object1, object2);
263
- }
264
- if (isDateObject(object1) && isDateObject(object2)) {
265
- return Object.is(object1.getTime(), object2.getTime());
266
- }
267
- const keys1 = Object.keys(object1);
268
- const keys2 = Object.keys(object2);
269
- if (keys1.length !== keys2.length) {
270
- return false;
271
- }
272
- if (isEmptyObjectWithCustomPrototype(object1, keys1) ||
273
- isEmptyObjectWithCustomPrototype(object2, keys2)) {
274
- return Object.is(object1, object2);
275
- }
276
- if (!keys1.length && Array.isArray(object1) !== Array.isArray(object2)) {
277
- return false;
278
- }
279
- const visitedPairs = visited.get(object1);
280
- if (visitedPairs && visitedPairs.has(object2)) {
281
- return true;
282
- }
283
- if (visitedPairs) {
284
- visitedPairs.add(object2);
285
- }
286
- else {
287
- const ws = new WeakSet();
288
- ws.add(object2);
289
- visited.set(object1, ws);
290
- }
291
- for (const key of keys1) {
292
- const val1 = object1[key];
293
- if (!(key in object2)) {
294
- return false;
295
- }
296
- if (key !== 'ref') {
297
- const val2 = object2[key];
298
- if ((isDateObject(val1) && isDateObject(val2)) ||
299
- ((isObject(val1) || Array.isArray(val1)) &&
300
- (isObject(val2) || Array.isArray(val2)))
301
- ? !deepEqual(val1, val2, visited)
302
- : !Object.is(val1, val2)) {
303
- return false;
304
- }
305
- }
306
- }
307
- return true;
308
- }
309
-
310
345
  /**
311
346
  * Custom hook to subscribe to field changes and isolate re-rendering at the component level.
312
347
  *
@@ -355,24 +390,39 @@ function useWatch(props) {
355
390
  }
356
391
  }
357
392
  }, [control._formValues, control._names, disabled, name]);
393
+ const { resyncIfNeeded, snapshot } = useResyncOnReconnect();
394
+ const _refreshValue = React.useRef(refreshValue);
395
+ _refreshValue.current = refreshValue;
396
+ const _getCurrentOutput = React.useRef(getCurrentOutput);
397
+ _getCurrentOutput.current = getCurrentOutput;
358
398
  useIsomorphicLayoutEffect(() => {
359
399
  if (_prevControl.current !== control ||
360
400
  !deepEqual(_prevName.current, name)) {
361
401
  _prevControl.current = control;
362
402
  _prevName.current = name;
363
- refreshValue();
403
+ _refreshValue.current();
404
+ }
405
+ else {
406
+ resyncIfNeeded(!disabled, () => _getCurrentOutput.current(), (currentValue) => {
407
+ updateValue(currentValue);
408
+ _computeFormValues.current = currentValue;
409
+ });
364
410
  }
365
- return control._subscribe({
411
+ const unsubscribe = control._subscribe({
366
412
  name,
367
413
  formState: {
368
414
  values: true,
369
415
  },
370
416
  exact,
371
417
  callback: (formState) => {
372
- refreshValue(formState.values);
418
+ _refreshValue.current(formState.values);
373
419
  },
374
420
  });
375
- }, [control, exact, name, refreshValue]);
421
+ return () => {
422
+ unsubscribe();
423
+ snapshot(!disabled, () => _getCurrentOutput.current());
424
+ };
425
+ }, [control, exact, name, disabled, resyncIfNeeded, snapshot]);
376
426
  React.useEffect(() => control._removeUnmounted());
377
427
  // If name or control changed for this render, synchronously reflect the
378
428
  // latest value so callers (like useController) see the correct value
@@ -380,17 +430,17 @@ function useWatch(props) {
380
430
  // Optimize: Check control reference first before expensive deepEqual
381
431
  const controlChanged = _prevControl.current !== control;
382
432
  const prevName = _prevName.current;
383
- // Cache the computed output to avoid duplicate calls within the same render
384
- // We include shouldReturnImmediate in deps to ensure proper recomputation
385
- const computedOutput = React.useMemo(() => {
433
+ // `null`/`undefined` are valid watched values, so a boolean flag (rather
434
+ // than a sentinel return value) decides whether to return the freshly
435
+ // computed output instead of the possibly-stale state value.
436
+ const shouldReturnImmediate = React.useMemo(() => {
386
437
  if (disabled) {
387
- return null;
438
+ return false;
388
439
  }
389
440
  const nameChanged = !controlChanged && !deepEqual(prevName, name);
390
- const shouldReturnImmediate = controlChanged || nameChanged;
391
- return shouldReturnImmediate ? getCurrentOutput() : null;
392
- }, [disabled, controlChanged, name, prevName, getCurrentOutput]);
393
- return computedOutput !== null ? computedOutput : value;
441
+ return controlChanged || nameChanged;
442
+ }, [disabled, controlChanged, name, prevName]);
443
+ return shouldReturnImmediate ? getCurrentOutput() : value;
394
444
  }
395
445
 
396
446
  /**
@@ -606,16 +656,12 @@ function useController(props) {
606
656
  */
607
657
  const Controller = (props) => props.render(useController(props));
608
658
 
609
- var generateId = () => {
610
- if (typeof crypto !== 'undefined' && crypto.randomUUID) {
611
- return crypto.randomUUID();
612
- }
613
- const d = typeof performance === 'undefined' ? Date.now() : performance.now() * 1000;
614
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
615
- const r = ((Math.random() * 16 + d) % 16) | 0;
616
- return (c == 'x' ? r : (r & 0x3) | 0x8).toString(16);
659
+ var generateId = () => typeof crypto !== 'undefined' && crypto.randomUUID
660
+ ? crypto.randomUUID()
661
+ : 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
662
+ const r = (Math.random() * 16) | 0;
663
+ return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
617
664
  });
618
- };
619
665
 
620
666
  var getFocusFieldName = (name, index, options = {}) => options.shouldFocus || isUndefined(options.shouldFocus)
621
667
  ? options.focusName ||
@@ -712,23 +758,25 @@ const defaultResult = {
712
758
  };
713
759
  const validResult = { value: true, isValid: true };
714
760
  var getCheckboxValue = (options) => {
715
- if (Array.isArray(options)) {
716
- if (options.length > 1) {
717
- const values = options
718
- .filter((option) => option && option.checked && !option.disabled)
719
- .map((option) => option.value);
720
- return { value: values, isValid: !!values.length };
721
- }
722
- return options[0].checked && !options[0].disabled
723
- ? // @ts-expect-error expected to work in the browser
724
- options[0].attributes && !isUndefined(options[0].attributes.value)
725
- ? isUndefined(options[0].value) || options[0].value === ''
726
- ? validResult
727
- : { value: options[0].value, isValid: true }
728
- : validResult
729
- : defaultResult;
730
- }
731
- return defaultResult;
761
+ if (!Array.isArray(options)) {
762
+ return defaultResult;
763
+ }
764
+ if (options.length > 1) {
765
+ const values = options
766
+ .filter((option) => option && option.checked && !option.disabled)
767
+ .map((option) => option.value);
768
+ return { value: values, isValid: !!values.length };
769
+ }
770
+ const option = options[0];
771
+ if (!option || !option.checked || option.disabled) {
772
+ return defaultResult;
773
+ }
774
+ if (!option.attributes || !('value' in option.attributes)) {
775
+ return validResult;
776
+ }
777
+ return isUndefined(option.value) || option.value === ''
778
+ ? validResult
779
+ : { value: option.value, isValid: true };
732
780
  };
733
781
 
734
782
  const defaultReturn = {
@@ -830,7 +878,9 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
830
878
  let exceedMin;
831
879
  const maxOutput = getValueAndMessage(max);
832
880
  const minOutput = getValueAndMessage(min);
833
- if (!isNullOrUndefined(inputValue) && !isNaN(inputValue)) {
881
+ if (!isNullOrUndefined(inputValue) &&
882
+ !isDateObject(inputValue) &&
883
+ !isNaN(inputValue)) {
834
884
  const valueNumber = ref.valueAsNumber ||
835
885
  (inputValue ? +inputValue : inputValue);
836
886
  if (!isNullOrUndefined(maxOutput.value)) {
@@ -944,7 +994,8 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
944
994
  }
945
995
  }
946
996
  }
947
- setCustomValidity(true);
997
+ const fieldError = error[name];
998
+ setCustomValidity(fieldError ? fieldError.message : true);
948
999
  return error;
949
1000
  };
950
1001
 
@@ -1223,8 +1274,8 @@ function useFieldArray(props) {
1223
1274
  setFields([...updatedFieldArrayValues]);
1224
1275
  control._setFieldArray(name, updatedFieldArrayValues, updateAt, {
1225
1276
  argA: index,
1226
- argB: updateValue,
1227
- }, true, false);
1277
+ argB: fillEmptyArray(value),
1278
+ });
1228
1279
  };
1229
1280
  const replace = (value) => {
1230
1281
  if (disabled) {
@@ -1238,9 +1289,11 @@ function useFieldArray(props) {
1238
1289
  };
1239
1290
  React.useEffect(() => {
1240
1291
  if (disabled) {
1292
+ control._state.actionArrayLengths.delete(name);
1241
1293
  return;
1242
1294
  }
1243
1295
  control._state.action = false;
1296
+ control._state.actionArrayLengths.delete(name);
1244
1297
  isWatched(name, control._names) &&
1245
1298
  control._subjects.state.next({
1246
1299
  ...control._formState,
@@ -1282,10 +1335,7 @@ function useFieldArray(props) {
1282
1335
  }
1283
1336
  else {
1284
1337
  const field = get(control._fields, name);
1285
- if (field &&
1286
- field._f &&
1287
- !(getValidationModes(control._options.reValidateMode).isOnSubmit &&
1288
- getValidationModes(control._options.mode).isOnSubmit)) {
1338
+ if (field && field._f) {
1289
1339
  validateField(field, control._names.disabled, control._formValues, control._options.criteriaMode === VALIDATION_MODE.all, control._options.shouldUseNativeValidation, true).then((error) => !isEmptyObject(error) &&
1290
1340
  control._subjects.state.next({
1291
1341
  errors: updateFieldArrayRootError(control._formState.errors, error, name),
@@ -1320,6 +1370,7 @@ function useFieldArray(props) {
1320
1370
  !get(control._formValues, name) && control._setFieldArray(name);
1321
1371
  }
1322
1372
  return () => {
1373
+ control._state.actionArrayLengths.delete(name);
1323
1374
  if (disabled) {
1324
1375
  return;
1325
1376
  }
@@ -1402,12 +1453,15 @@ function useFieldArray(props) {
1402
1453
  */
1403
1454
  const FieldArray = (props) => props.render(useFieldArray(props));
1404
1455
 
1456
+ const isFileLike = (value) => (typeof Blob !== 'undefined' && value instanceof Blob) ||
1457
+ (typeof File !== 'undefined' && value instanceof File);
1405
1458
  const flatten = (obj) => {
1406
1459
  const output = {};
1407
1460
  for (const key of Object.keys(obj)) {
1408
1461
  if (isObjectType(obj[key]) &&
1409
1462
  obj[key] !== null &&
1410
- !isDateObject(obj[key])) {
1463
+ !isDateObject(obj[key]) &&
1464
+ !isFileLike(obj[key])) {
1411
1465
  const nested = flatten(obj[key]);
1412
1466
  for (const nestedKey of Object.keys(nested)) {
1413
1467
  output[`${key}.${nestedKey}`] = nested[nestedKey];
@@ -1503,10 +1557,11 @@ const useFormContext = () => React.useContext(HookFormContext);
1503
1557
  * }
1504
1558
  * ```
1505
1559
  */
1506
- const FormProvider = ({ children, watch, getValues, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, resetDefaultValues, handleSubmit, unregister, control, register, setFocus, subscribe, }) => {
1560
+ const FormProvider = ({ children, watch, getValues, getErrors, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, resetDefaultValues, handleSubmit, unregister, control, register, setFocus, subscribe, }) => {
1507
1561
  const memoizedValue = React.useMemo(() => ({
1508
1562
  watch,
1509
1563
  getValues,
1564
+ getErrors,
1510
1565
  getFieldState,
1511
1566
  setError,
1512
1567
  clearErrors,
@@ -1527,6 +1582,7 @@ const FormProvider = ({ children, watch, getValues, getFieldState, setError, cle
1527
1582
  clearErrors,
1528
1583
  control,
1529
1584
  formState,
1585
+ getErrors,
1530
1586
  getFieldState,
1531
1587
  getValues,
1532
1588
  handleSubmit,
@@ -1714,6 +1770,24 @@ function extractFormValues(fieldsState, formValues) {
1714
1770
  return values;
1715
1771
  }
1716
1772
 
1773
+ const hasOwn = (value, key) => value !== null &&
1774
+ isObjectType(value) &&
1775
+ Object.prototype.hasOwnProperty.call(value, key);
1776
+ var has = (object, path) => {
1777
+ if (!path) {
1778
+ return false;
1779
+ }
1780
+ let result = object;
1781
+ for (const key of isKey(path) ? [path] : stringToPath(path)) {
1782
+ if (!hasOwn(result, key)) {
1783
+ // `get` also resolves a path held as a single literal key, eg `{ 'a.b': 1 }`
1784
+ return hasOwn(object, path);
1785
+ }
1786
+ result = result[key];
1787
+ }
1788
+ return true;
1789
+ };
1790
+
1717
1791
  var isMultipleSelect = (element) => element.type === `select-multiple`;
1718
1792
 
1719
1793
  var isRadioOrCheckbox = (ref) => isRadioInput(ref) || isCheckBoxInput(ref);
@@ -1813,6 +1887,18 @@ function getDirtyFields(data, formValues, dirtyFieldsFromValues, fieldRefs) {
1813
1887
  return dirtyFieldsFromValues;
1814
1888
  }
1815
1889
 
1890
+ var getFieldArrayItemNames = (names, name) => {
1891
+ const segments = name.split('.');
1892
+ const matches = [];
1893
+ let prefix = segments[0];
1894
+ for (let i = 1; i < segments.length; prefix += '.' + segments[i++]) {
1895
+ if (!isNaN(+segments[i]) && names.has(prefix)) {
1896
+ matches.push(`${prefix}.${segments[i]}`);
1897
+ }
1898
+ }
1899
+ return matches;
1900
+ };
1901
+
1816
1902
  var getFieldValueAs = (value, { valueAsNumber, valueAsDate, setValueAs }) => isUndefined(value)
1817
1903
  ? value
1818
1904
  : valueAsNumber
@@ -1888,10 +1974,11 @@ var hasPromiseValidation = (fieldReference) => {
1888
1974
 
1889
1975
  var hasValidation = (options) => options.mount &&
1890
1976
  (options.required ||
1891
- options.min ||
1892
- options.max ||
1893
- options.maxLength ||
1894
- options.minLength ||
1977
+ (!isUndefined(options.required) && options.required !== false) ||
1978
+ !isUndefined(options.min) ||
1979
+ !isUndefined(options.max) ||
1980
+ !isUndefined(options.maxLength) ||
1981
+ !isUndefined(options.minLength) ||
1895
1982
  options.pattern ||
1896
1983
  options.validate);
1897
1984
 
@@ -1964,7 +2051,12 @@ var skipValidation = (isBlurEvent, isTouched, isSubmitted, reValidateMode, mode)
1964
2051
  return true;
1965
2052
  };
1966
2053
 
1967
- var unsetEmptyArray = (ref, name) => !compact(get(ref, name)).length && unset(ref, name);
2054
+ var unsetEmptyArray = (ref, name) => {
2055
+ const array = get(ref, name);
2056
+ !compact(array).length &&
2057
+ !(array === null || array === void 0 ? void 0 : array.root) &&
2058
+ unset(ref, name);
2059
+ };
1968
2060
 
1969
2061
  const defaultOptions = {
1970
2062
  mode: VALIDATION_MODE.onSubmit,
@@ -2013,6 +2105,7 @@ function createFormControl(props = {}) {
2013
2105
  : cloneObject(_defaultValues);
2014
2106
  let _state = {
2015
2107
  action: false,
2108
+ actionArrayLengths: new Map(),
2016
2109
  mount: false,
2017
2110
  watch: false,
2018
2111
  keepIsValid: false,
@@ -2089,7 +2182,7 @@ function createFormControl(props = {}) {
2089
2182
  _proxyFormState.validatingFields ||
2090
2183
  _proxySubscribeFormState.isValidating ||
2091
2184
  _proxySubscribeFormState.validatingFields)) {
2092
- (names || Array.from(_names.mount)).forEach((name) => {
2185
+ (names || _names.mount).forEach((name) => {
2093
2186
  if (name) {
2094
2187
  isValidating
2095
2188
  ? set(_formState.validatingFields, name, isValidating)
@@ -2108,21 +2201,30 @@ function createFormControl(props = {}) {
2108
2201
  const _setFieldArray = (name, values = [], method, args, shouldSetValues = true, shouldUpdateFieldsAndState = true) => {
2109
2202
  if (args && method && !_options.disabled) {
2110
2203
  _state.action = true;
2111
- if (shouldUpdateFieldsAndState && Array.isArray(get(_fields, name))) {
2112
- const fieldValues = method(get(_fields, name), args.argA, args.argB);
2204
+ const fields = get(_fields, name);
2205
+ if (!_state.actionArrayLengths.has(name)) {
2206
+ _state.actionArrayLengths.set(name, Array.isArray(fields) ? fields.length : 0);
2207
+ }
2208
+ if (shouldUpdateFieldsAndState && Array.isArray(fields)) {
2209
+ const fieldValues = method(fields, args.argA, args.argB);
2113
2210
  shouldSetValues && set(_fields, name, fieldValues);
2114
2211
  }
2115
- if (shouldUpdateFieldsAndState &&
2116
- Array.isArray(get(_formState.errors, name))) {
2117
- const errors = method(get(_formState.errors, name), args.argA, args.argB);
2212
+ const fieldArrayErrors = get(_formState.errors, name);
2213
+ if (shouldUpdateFieldsAndState && Array.isArray(fieldArrayErrors)) {
2214
+ const rootError = fieldArrayErrors.root;
2215
+ const errors = method(fieldArrayErrors, args.argA, args.argB) || fieldArrayErrors;
2216
+ if (rootError) {
2217
+ errors.root = rootError;
2218
+ }
2118
2219
  shouldSetValues && set(_formState.errors, name, errors);
2119
2220
  unsetEmptyArray(_formState.errors, name);
2120
2221
  }
2222
+ const touchedFieldsArray = get(_formState.touchedFields, name);
2121
2223
  if ((_proxyFormState.touchedFields ||
2122
2224
  _proxySubscribeFormState.touchedFields) &&
2123
2225
  shouldUpdateFieldsAndState &&
2124
- Array.isArray(get(_formState.touchedFields, name))) {
2125
- const touchedFields = method(get(_formState.touchedFields, name), args.argA, args.argB);
2226
+ Array.isArray(touchedFieldsArray)) {
2227
+ const touchedFields = method(touchedFieldsArray, args.argA, args.argB);
2126
2228
  shouldSetValues && set(_formState.touchedFields, name, touchedFields);
2127
2229
  }
2128
2230
  if (_proxyFormState.dirtyFields || _proxySubscribeFormState.dirtyFields) {
@@ -2170,10 +2272,40 @@ function createFormControl(props = {}) {
2170
2272
  }
2171
2273
  return false;
2172
2274
  };
2275
+ const isStaleArrayIndex = (name) => {
2276
+ if (!_state.actionArrayLengths.size) {
2277
+ return false;
2278
+ }
2279
+ const segments = isKey(name) ? [name] : stringToPath(name);
2280
+ let node = _formValues;
2281
+ let path = '';
2282
+ let ownerDepth = -1;
2283
+ let ownerPreActionLength = 0;
2284
+ for (let i = 0; i < segments.length; i++) {
2285
+ if (isNullOrUndefined(node)) {
2286
+ return false;
2287
+ }
2288
+ const key = segments[i];
2289
+ path = path ? `${path}.${key}` : key;
2290
+ if (Array.isArray(node) && +key >= node.length) {
2291
+ return ownerDepth === -1
2292
+ ? false
2293
+ : i === ownerDepth
2294
+ ? +key < ownerPreActionLength
2295
+ : true;
2296
+ }
2297
+ if (_state.actionArrayLengths.has(path)) {
2298
+ ownerDepth = i + 1;
2299
+ ownerPreActionLength = _state.actionArrayLengths.get(path);
2300
+ }
2301
+ node = node[key];
2302
+ }
2303
+ return false;
2304
+ };
2173
2305
  const updateValidAndValue = (name, shouldSkipSetValueAs, value, ref) => {
2174
2306
  const field = get(_fields, name);
2175
2307
  if (field) {
2176
- if (hasExplicitNullIntermediate(name)) {
2308
+ if (hasExplicitNullIntermediate(name) || isStaleArrayIndex(name)) {
2177
2309
  return;
2178
2310
  }
2179
2311
  const wasUnsetInFormValues = isUndefined(get(_formValues, name));
@@ -2278,10 +2410,6 @@ function createFormControl(props = {}) {
2278
2410
  errors: _formState.errors,
2279
2411
  name,
2280
2412
  };
2281
- _formState = {
2282
- ..._formState,
2283
- ...updatedFormState,
2284
- };
2285
2413
  _subjects.state.next(updatedFormState);
2286
2414
  }
2287
2415
  };
@@ -2421,14 +2549,12 @@ function createFormControl(props = {}) {
2421
2549
  const _getWatch = (names, defaultValue, isGlobal) => generateWatchOutput(names, _names, {
2422
2550
  ...(_state.mount
2423
2551
  ? _formValues
2424
- : isUndefined(defaultValue)
2552
+ : isUndefined(defaultValue) || isString(names)
2425
2553
  ? _defaultValues
2426
- : isString(names)
2427
- ? { [names]: defaultValue }
2428
- : defaultValue),
2554
+ : defaultValue),
2429
2555
  }, isGlobal, defaultValue);
2430
2556
  const _getFieldArray = (name) => compact(get(_state.mount ? _formValues : _defaultValues, name, _options.shouldUnregister ? get(_defaultValues, name, []) : []));
2431
- const setFieldValue = (name, value, options = {}, skipClone = false, skipRender = false) => {
2557
+ const setFieldValue = (name, value, options = {}, skipClone = false, skipRender = false, skipValueRender = false) => {
2432
2558
  const field = get(_fields, name);
2433
2559
  let fieldValue = value;
2434
2560
  if (field) {
@@ -2466,7 +2592,7 @@ function createFormControl(props = {}) {
2466
2592
  }
2467
2593
  else {
2468
2594
  fieldReference.ref.value = fieldValue;
2469
- if (!fieldReference.ref.type && !skipRender) {
2595
+ if (!fieldReference.ref.type && !skipRender && !skipValueRender) {
2470
2596
  _subjects.state.next({
2471
2597
  name,
2472
2598
  values: skipClone ? _formValues : cloneObject(_formValues),
@@ -2482,7 +2608,7 @@ function createFormControl(props = {}) {
2482
2608
  delayError: options.delayError,
2483
2609
  });
2484
2610
  };
2485
- const setFieldValues = (name, value, options, skipClone = false, skipRender = false) => {
2611
+ const setFieldValues = (name, value, options, skipClone = false, skipRender = false, skipValueRender = false) => {
2486
2612
  if (_names.array.has(name)) {
2487
2613
  _subjects.array.next({
2488
2614
  name,
@@ -2500,8 +2626,8 @@ function createFormControl(props = {}) {
2500
2626
  isObject(fieldValue) ||
2501
2627
  (field && !field._f)) &&
2502
2628
  !isDateObject(fieldValue)
2503
- ? setFieldValues(fieldName, fieldValue, options, skipClone, skipRender)
2504
- : setFieldValue(fieldName, fieldValue, options, skipClone, skipRender);
2629
+ ? setFieldValues(fieldName, fieldValue, options, skipClone, skipRender, skipValueRender)
2630
+ : setFieldValue(fieldName, fieldValue, options, skipClone, skipRender, skipValueRender);
2505
2631
  }
2506
2632
  };
2507
2633
  const _setValue = (name, value, options, skipClone, skipStateEmit = false) => {
@@ -2536,11 +2662,12 @@ function createFormControl(props = {}) {
2536
2662
  else {
2537
2663
  const isEmpty = (Array.isArray(cloneValue) && !cloneValue.length) ||
2538
2664
  isEmptyObject(cloneValue);
2665
+ const skipValueRender = !isValueUnchanged && !skipStateEmit;
2539
2666
  if (!field || field._f || isNullOrUndefined(cloneValue) || isEmpty) {
2540
- setFieldValue(name, cloneValue, options, skipClone, skipStateEmit);
2667
+ setFieldValue(name, cloneValue, options, skipClone, skipStateEmit, skipValueRender);
2541
2668
  }
2542
2669
  else {
2543
- setFieldValues(name, cloneValue, options, skipClone, skipStateEmit);
2670
+ setFieldValues(name, cloneValue, options, skipClone, skipStateEmit, skipValueRender);
2544
2671
  }
2545
2672
  }
2546
2673
  if (!isValueUnchanged && !skipStateEmit) {
@@ -2551,6 +2678,11 @@ function createFormControl(props = {}) {
2551
2678
  name: _state.mount || watched ? name : undefined,
2552
2679
  values,
2553
2680
  });
2681
+ if (!isFieldArray) {
2682
+ for (const itemName of getFieldArrayItemNames(_names.array, name)) {
2683
+ _subjects.state.next({ name: itemName, values });
2684
+ }
2685
+ }
2554
2686
  }
2555
2687
  };
2556
2688
  const setValue = (name, value, options = {}) => _setValue(name, value, options, false);
@@ -2563,10 +2695,9 @@ function createFormControl(props = {}) {
2563
2695
  ..._formValues,
2564
2696
  ...updatedFormValues,
2565
2697
  };
2566
- const flattenedUpdates = flatten(updatedFormValues);
2567
2698
  for (const fieldName of _names.mount) {
2568
- if (fieldName in flattenedUpdates) {
2569
- _setValue(fieldName, flattenedUpdates[fieldName], options, true, true);
2699
+ if (has(updatedFormValues, fieldName)) {
2700
+ _setValue(fieldName, get(updatedFormValues, fieldName), options, true, true);
2570
2701
  }
2571
2702
  }
2572
2703
  _subjects.state.next({
@@ -2766,13 +2897,22 @@ function createFormControl(props = {}) {
2766
2897
  ? get(values, fieldNames)
2767
2898
  : fieldNames.map((name) => get(values, name));
2768
2899
  };
2769
- const getFieldState = (name, formState) => ({
2770
- invalid: !!get((formState || _formState).errors, name),
2771
- isDirty: !!get((formState || _formState).dirtyFields, name),
2772
- error: get((formState || _formState).errors, name),
2773
- isValidating: !!get(_formState.validatingFields, name),
2774
- isTouched: !!get((formState || _formState).touchedFields, name),
2775
- });
2900
+ const getErrors = (fieldNames) => isUndefined(fieldNames)
2901
+ ? { ..._formState.errors }
2902
+ : isString(fieldNames)
2903
+ ? get(_formState.errors, fieldNames)
2904
+ : fieldNames.map((name) => get(_formState.errors, name));
2905
+ const getFieldState = (name, formState) => {
2906
+ const targetFormState = formState || _formState;
2907
+ const error = get(targetFormState.errors, name);
2908
+ return {
2909
+ invalid: !!error,
2910
+ isDirty: !!get(targetFormState.dirtyFields, name),
2911
+ error,
2912
+ isValidating: !!get(_formState.validatingFields, name),
2913
+ isTouched: !!get(targetFormState.touchedFields, name),
2914
+ };
2915
+ };
2776
2916
  const clearErrors = (name) => {
2777
2917
  const names = name ? convertToArrayPayload(name) : undefined;
2778
2918
  names === null || names === void 0 ? void 0 : names.forEach((inputName) => unset(_formState.errors, inputName));
@@ -2892,12 +3032,13 @@ function createFormControl(props = {}) {
2892
3032
  !options.keepDefaultValue &&
2893
3033
  unset(_defaultValues, fieldName);
2894
3034
  }
2895
- _subjects.state.next({
2896
- values: cloneObject(_formValues),
2897
- });
3035
+ _valuesSubscriberCount &&
3036
+ _subjects.state.next({
3037
+ values: cloneObject(_formValues),
3038
+ });
2898
3039
  _subjects.state.next({
2899
3040
  ..._formState,
2900
- ...(!options.keepDirty ? {} : { isDirty: _getDirty() }),
3041
+ ...(options.keepDirty ? {} : { isDirty: _getDirty() }),
2901
3042
  });
2902
3043
  !options.keepIsValid && _setValid();
2903
3044
  };
@@ -3123,7 +3264,7 @@ function createFormControl(props = {}) {
3123
3264
  ..._names.mount,
3124
3265
  ...collectDirtyFieldNames(getDirtyFields(_defaultValues, _formValues, undefined, fieldRefs), _formState.dirtyFields),
3125
3266
  ]);
3126
- for (const fieldName of Array.from(fieldsToCheck)) {
3267
+ for (const fieldName of fieldsToCheck) {
3127
3268
  const isDirty = get(_formState.dirtyFields, fieldName);
3128
3269
  const existingValue = get(_formValues, fieldName);
3129
3270
  const newValue = get(values, fieldName);
@@ -3202,6 +3343,7 @@ function createFormControl(props = {}) {
3202
3343
  _state.watch = !!_options.shouldUnregister;
3203
3344
  _state.keepIsValid = !!keepStateOptions.keepIsValid;
3204
3345
  _state.action = false;
3346
+ _state.actionArrayLengths.clear();
3205
3347
  if (!keepStateOptions.keepErrors) {
3206
3348
  _formState.errors = {};
3207
3349
  }
@@ -3274,6 +3416,7 @@ function createFormControl(props = {}) {
3274
3416
  ...formState,
3275
3417
  };
3276
3418
  };
3419
+ _subjects.state.subscribe({ next: _setFormState });
3277
3420
  const _resetDefaultValues = () => isFunction(_options.defaultValues) &&
3278
3421
  _options.defaultValues().then((values) => {
3279
3422
  reset(values, _options.resetOptions);
@@ -3364,6 +3507,7 @@ function createFormControl(props = {}) {
3364
3507
  setValue,
3365
3508
  setValues,
3366
3509
  getValues,
3510
+ getErrors,
3367
3511
  reset,
3368
3512
  resetField,
3369
3513
  resetDefaultValues,
@@ -3443,8 +3587,14 @@ function useForm(props = {}) {
3443
3587
  }
3444
3588
  const control = _formControl.current.control;
3445
3589
  control._options = props;
3590
+ const { resyncIfNeeded, snapshot } = useResyncOnReconnect();
3446
3591
  useIsomorphicLayoutEffect(() => {
3447
- const sub = control._subscribe({
3592
+ const getCurrentFormState = () => ({
3593
+ ...control._formState,
3594
+ defaultValues: control._defaultValues,
3595
+ });
3596
+ resyncIfNeeded(true, getCurrentFormState, updateFormState);
3597
+ const unsubscribe = control._subscribe({
3448
3598
  formState: control._proxyFormState,
3449
3599
  callback: () => updateFormState({
3450
3600
  ...control._formState,
@@ -3457,8 +3607,11 @@ function useForm(props = {}) {
3457
3607
  isReady: true,
3458
3608
  }));
3459
3609
  control._formState.isReady = true;
3460
- return sub;
3461
- }, [control]);
3610
+ return () => {
3611
+ unsubscribe();
3612
+ snapshot(true, getCurrentFormState);
3613
+ };
3614
+ }, [control, resyncIfNeeded, snapshot]);
3462
3615
  React.useEffect(() => control._disableForm(props.disabled), [control, props.disabled]);
3463
3616
  React.useEffect(() => {
3464
3617
  if (props.mode) {