@depup/react-hook-form 7.83.0-depup.0 → 7.85.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 (46) hide show
  1. package/README.md +2 -2
  2. package/changes.json +1 -1
  3. package/dist/fieldArray.d.ts +2 -1
  4. package/dist/fieldArray.d.ts.map +1 -1
  5. package/dist/form.d.ts +1 -1
  6. package/dist/form.d.ts.map +1 -1
  7. package/dist/formStateSubscribe.d.ts +8 -3
  8. package/dist/formStateSubscribe.d.ts.map +1 -1
  9. package/dist/index.cjs.js +1 -1
  10. package/dist/index.cjs.js.map +1 -1
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.esm.mjs +311 -150
  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/collectDirtyFieldNames.d.ts +3 -0
  18. package/dist/logic/collectDirtyFieldNames.d.ts.map +1 -0
  19. package/dist/logic/createFormControl.d.ts.map +1 -1
  20. package/dist/logic/getFieldValue.d.ts.map +1 -1
  21. package/dist/logic/iterateFieldsByAction.d.ts.map +1 -1
  22. package/dist/logic/shouldRenderFormState.d.ts.map +1 -1
  23. package/dist/logic/unsetEmptyArray.d.ts +1 -1
  24. package/dist/logic/unsetEmptyArray.d.ts.map +1 -1
  25. package/dist/logic/validateField.d.ts.map +1 -1
  26. package/dist/react-server.esm.mjs +102 -26
  27. package/dist/react-server.esm.mjs.map +1 -1
  28. package/dist/types/errors.d.ts +6 -1
  29. package/dist/types/errors.d.ts.map +1 -1
  30. package/dist/types/form.d.ts +8 -6
  31. package/dist/types/form.d.ts.map +1 -1
  32. package/dist/useFieldArray.d.ts.map +1 -1
  33. package/dist/useForm.d.ts.map +1 -1
  34. package/dist/useFormState.d.ts.map +1 -1
  35. package/dist/useResyncOnReconnect.d.ts +5 -0
  36. package/dist/useResyncOnReconnect.d.ts.map +1 -0
  37. package/dist/useWatch.d.ts.map +1 -1
  38. package/dist/utils/formData.d.ts +3 -0
  39. package/dist/utils/formData.d.ts.map +1 -0
  40. package/dist/utils/json.d.ts +9 -0
  41. package/dist/utils/json.d.ts.map +1 -0
  42. package/package.json +4 -10
  43. package/dist/logic/getNodeParentName.d.ts +0 -3
  44. package/dist/logic/getNodeParentName.d.ts.map +0 -1
  45. package/dist/utils/deepMerge.d.ts +0 -2
  46. package/dist/utils/deepMerge.d.ts.map +0 -1
@@ -170,6 +170,82 @@ const useIsomorphicLayoutEffect = isWeb
170
170
  ? React.useLayoutEffect
171
171
  : React.useEffect;
172
172
 
173
+ var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
174
+
175
+ const isEmptyObjectWithCustomPrototype = (object, keys) => keys.length === 0 && !Array.isArray(object) && !isPlainObject(object);
176
+ function deepEqual(object1, object2, visited = new WeakMap()) {
177
+ if (object1 === object2) {
178
+ return true;
179
+ }
180
+ if (isPrimitive(object1) || isPrimitive(object2)) {
181
+ return Object.is(object1, object2);
182
+ }
183
+ if (isDateObject(object1) && isDateObject(object2)) {
184
+ return Object.is(object1.getTime(), object2.getTime());
185
+ }
186
+ const keys1 = Object.keys(object1);
187
+ const keys2 = Object.keys(object2);
188
+ if (keys1.length !== keys2.length) {
189
+ return false;
190
+ }
191
+ if (isEmptyObjectWithCustomPrototype(object1, keys1) ||
192
+ isEmptyObjectWithCustomPrototype(object2, keys2)) {
193
+ return Object.is(object1, object2);
194
+ }
195
+ if (!keys1.length && Array.isArray(object1) !== Array.isArray(object2)) {
196
+ return false;
197
+ }
198
+ const visitedPairs = visited.get(object1);
199
+ if (visitedPairs && visitedPairs.has(object2)) {
200
+ return true;
201
+ }
202
+ if (visitedPairs) {
203
+ visitedPairs.add(object2);
204
+ }
205
+ else {
206
+ const ws = new WeakSet();
207
+ ws.add(object2);
208
+ visited.set(object1, ws);
209
+ }
210
+ for (const key of keys1) {
211
+ const val1 = object1[key];
212
+ if (!(key in object2)) {
213
+ return false;
214
+ }
215
+ if (key !== 'ref') {
216
+ const val2 = object2[key];
217
+ if ((isDateObject(val1) && isDateObject(val2)) ||
218
+ ((isObject(val1) || Array.isArray(val1)) &&
219
+ (isObject(val2) || Array.isArray(val2)))
220
+ ? !deepEqual(val1, val2, visited)
221
+ : !Object.is(val1, val2)) {
222
+ return false;
223
+ }
224
+ }
225
+ }
226
+ return true;
227
+ }
228
+
229
+ function useResyncOnReconnect() {
230
+ const _connected = React.useRef(false);
231
+ const _prevValue = React.useRef(undefined);
232
+ const resyncIfNeeded = React.useCallback((enabled, getCurrentValue, setValue) => {
233
+ if (enabled && _connected.current) {
234
+ const currentValue = getCurrentValue();
235
+ if (!deepEqual(_prevValue.current, currentValue)) {
236
+ setValue(currentValue);
237
+ }
238
+ }
239
+ _connected.current = true;
240
+ }, []);
241
+ const snapshot = React.useCallback((enabled, getCurrentValue) => {
242
+ if (enabled) {
243
+ _prevValue.current = cloneObject(getCurrentValue());
244
+ }
245
+ }, []);
246
+ return { resyncIfNeeded, snapshot };
247
+ }
248
+
173
249
  /**
174
250
  * 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
251
  *
@@ -217,19 +293,31 @@ function useFormState(props) {
217
293
  isValid: false,
218
294
  errors: false,
219
295
  });
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]);
296
+ const { resyncIfNeeded, snapshot } = useResyncOnReconnect();
297
+ useIsomorphicLayoutEffect(() => {
298
+ const getCurrentFormState = () => ({
299
+ ...control._formState,
300
+ defaultValues: control._defaultValues,
301
+ });
302
+ resyncIfNeeded(!disabled, getCurrentFormState, updateFormState);
303
+ const unsubscribe = control._subscribe({
304
+ name,
305
+ formState: _localProxyFormState.current,
306
+ exact,
307
+ callback: (formState) => {
308
+ !disabled &&
309
+ updateFormState({
310
+ ...control._formState,
311
+ ...formState,
312
+ defaultValues: control._defaultValues,
313
+ });
314
+ },
315
+ });
316
+ return () => {
317
+ unsubscribe();
318
+ snapshot(!disabled, getCurrentFormState);
319
+ };
320
+ }, [name, disabled, exact, resyncIfNeeded, snapshot]);
233
321
  React.useEffect(() => {
234
322
  _localProxyFormState.current.isValid && control._setValid(true);
235
323
  }, [control]);
@@ -251,62 +339,6 @@ var generateWatchOutput = (names, _names, formValues, isGlobal, defaultValue) =>
251
339
  return formValues;
252
340
  };
253
341
 
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
342
  /**
311
343
  * Custom hook to subscribe to field changes and isolate re-rendering at the component level.
312
344
  *
@@ -355,24 +387,39 @@ function useWatch(props) {
355
387
  }
356
388
  }
357
389
  }, [control._formValues, control._names, disabled, name]);
390
+ const { resyncIfNeeded, snapshot } = useResyncOnReconnect();
391
+ const _refreshValue = React.useRef(refreshValue);
392
+ _refreshValue.current = refreshValue;
393
+ const _getCurrentOutput = React.useRef(getCurrentOutput);
394
+ _getCurrentOutput.current = getCurrentOutput;
358
395
  useIsomorphicLayoutEffect(() => {
359
396
  if (_prevControl.current !== control ||
360
397
  !deepEqual(_prevName.current, name)) {
361
398
  _prevControl.current = control;
362
399
  _prevName.current = name;
363
- refreshValue();
400
+ _refreshValue.current();
401
+ }
402
+ else {
403
+ resyncIfNeeded(!disabled, () => _getCurrentOutput.current(), (currentValue) => {
404
+ updateValue(currentValue);
405
+ _computeFormValues.current = currentValue;
406
+ });
364
407
  }
365
- return control._subscribe({
408
+ const unsubscribe = control._subscribe({
366
409
  name,
367
410
  formState: {
368
411
  values: true,
369
412
  },
370
413
  exact,
371
414
  callback: (formState) => {
372
- refreshValue(formState.values);
415
+ _refreshValue.current(formState.values);
373
416
  },
374
417
  });
375
- }, [control, exact, name, refreshValue]);
418
+ return () => {
419
+ unsubscribe();
420
+ snapshot(!disabled, () => _getCurrentOutput.current());
421
+ };
422
+ }, [control, exact, name, disabled, resyncIfNeeded, snapshot]);
376
423
  React.useEffect(() => control._removeUnmounted());
377
424
  // If name or control changed for this render, synchronously reflect the
378
425
  // latest value so callers (like useController) see the correct value
@@ -644,9 +691,12 @@ var isWatched = (name, _names, isBlurEvent) => {
644
691
 
645
692
  const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
646
693
  for (const key of fieldsNames || Object.keys(fields)) {
647
- const field = get(fields, key);
694
+ if (key === '_f') {
695
+ continue;
696
+ }
697
+ const field = fieldsNames ? get(fields, key) : fields[key];
648
698
  if (field) {
649
- const { _f, ...currentField } = field;
699
+ const { _f } = field;
650
700
  if (_f) {
651
701
  if (_f.refs && _f.refs[0] && action(_f.refs[0], key) && !abortEarly) {
652
702
  return true;
@@ -655,13 +705,13 @@ const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
655
705
  return true;
656
706
  }
657
707
  else {
658
- if (iterateFieldsByAction(currentField, action)) {
708
+ if (iterateFieldsByAction(field, action)) {
659
709
  break;
660
710
  }
661
711
  }
662
712
  }
663
- else if (isObject(currentField)) {
664
- if (iterateFieldsByAction(currentField, action)) {
713
+ else if (isObject(field) || Array.isArray(field)) {
714
+ if (iterateFieldsByAction(field, action)) {
665
715
  break;
666
716
  }
667
717
  }
@@ -827,7 +877,9 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
827
877
  let exceedMin;
828
878
  const maxOutput = getValueAndMessage(max);
829
879
  const minOutput = getValueAndMessage(min);
830
- if (!isNullOrUndefined(inputValue) && !isNaN(inputValue)) {
880
+ if (!isNullOrUndefined(inputValue) &&
881
+ !isDateObject(inputValue) &&
882
+ !isNaN(inputValue)) {
831
883
  const valueNumber = ref.valueAsNumber ||
832
884
  (inputValue ? +inputValue : inputValue);
833
885
  if (!isNullOrUndefined(maxOutput.value)) {
@@ -998,7 +1050,7 @@ var swapArrayAt = (data, indexA, indexB) => {
998
1050
  };
999
1051
 
1000
1052
  function baseGet(object, updatePath) {
1001
- const length = updatePath.slice(0, -1).length;
1053
+ const length = updatePath.length - 1;
1002
1054
  let index = 0;
1003
1055
  while (index < length) {
1004
1056
  if (isNullOrUndefined(object)) {
@@ -1235,9 +1287,11 @@ function useFieldArray(props) {
1235
1287
  };
1236
1288
  React.useEffect(() => {
1237
1289
  if (disabled) {
1290
+ control._state.actionArrayLengths.delete(name);
1238
1291
  return;
1239
1292
  }
1240
1293
  control._state.action = false;
1294
+ control._state.actionArrayLengths.delete(name);
1241
1295
  isWatched(name, control._names) &&
1242
1296
  control._subjects.state.next({
1243
1297
  ...control._formState,
@@ -1317,6 +1371,7 @@ function useFieldArray(props) {
1317
1371
  !get(control._formValues, name) && control._setFieldArray(name);
1318
1372
  }
1319
1373
  return () => {
1374
+ control._state.actionArrayLengths.delete(name);
1320
1375
  if (disabled) {
1321
1376
  return;
1322
1377
  }
@@ -1417,6 +1472,26 @@ const flatten = (obj) => {
1417
1472
  return output;
1418
1473
  };
1419
1474
 
1475
+ function jsonToFormData(json) {
1476
+ const result = new FormData();
1477
+ const flattenFormValues = flatten(json);
1478
+ for (const key in flattenFormValues) {
1479
+ result.append(key, flattenFormValues[key]);
1480
+ }
1481
+ return result;
1482
+ }
1483
+
1484
+ function safeJSONStringify(value) {
1485
+ try {
1486
+ return JSON.stringify(value);
1487
+ }
1488
+ catch (_a) {
1489
+ return '';
1490
+ }
1491
+ }
1492
+
1493
+ function noop() { }
1494
+
1420
1495
  const HookFormContext = React.createContext(null);
1421
1496
  HookFormContext.displayName = 'HookFormContext';
1422
1497
  /**
@@ -1525,6 +1600,9 @@ const FormProvider = ({ children, watch, getValues, getFieldState, setError, cle
1525
1600
  };
1526
1601
 
1527
1602
  const POST_REQUEST = 'post';
1603
+ function defaultValidateStatus(status) {
1604
+ return status >= 200 && status < 300;
1605
+ }
1528
1606
  /**
1529
1607
  * Form component to manage submission.
1530
1608
  *
@@ -1550,21 +1628,11 @@ const POST_REQUEST = 'post';
1550
1628
  function Form(props) {
1551
1629
  const methods = useFormContext();
1552
1630
  const [mounted, setMounted] = React.useState(false);
1553
- const { control = methods.control, onSubmit, children, action, method = POST_REQUEST, headers, encType, onError, render, onSuccess, validateStatus, ...rest } = props;
1554
- const submit = React.useCallback(async (event) => {
1555
- let hasError = false;
1556
- let type = '';
1557
- await control.handleSubmit(async (data) => {
1558
- const formData = new FormData();
1559
- let formDataJson = '';
1560
- try {
1561
- formDataJson = JSON.stringify(data);
1562
- }
1563
- catch (_a) { }
1564
- const flattenFormValues = flatten(data);
1565
- for (const key in flattenFormValues) {
1566
- formData.append(key, flattenFormValues[key]);
1567
- }
1631
+ const { control = methods.control, onSubmit = noop, children, action, method = POST_REQUEST, headers, encType, onError = noop, render, onSuccess = noop, validateStatus = defaultValidateStatus, ...rest } = props;
1632
+ const handleSubmit = React.useMemo(() => {
1633
+ return control.handleSubmit(async (data, event) => {
1634
+ const formData = jsonToFormData(data);
1635
+ const formDataJson = safeJSONStringify(data);
1568
1636
  if (onSubmit) {
1569
1637
  await onSubmit({
1570
1638
  data,
@@ -1574,48 +1642,48 @@ function Form(props) {
1574
1642
  formDataJson,
1575
1643
  });
1576
1644
  }
1577
- if (action) {
1645
+ if (isString(action)) {
1578
1646
  try {
1579
- const shouldStringifySubmissionData = [
1580
- headers && headers['Content-Type'],
1581
- encType,
1582
- ].some((value) => value && value.includes('json'));
1583
- const response = await fetch(String(action), {
1647
+ const shouldStringifySubmissionData = (headers &&
1648
+ headers['Content-Type'] &&
1649
+ headers['Content-Type'].includes('json')) ||
1650
+ (encType && encType.includes('json'));
1651
+ const response = await fetch(action, {
1584
1652
  method,
1585
1653
  headers: {
1586
1654
  ...headers,
1587
- ...(encType && encType !== 'multipart/form-data'
1588
- ? { 'Content-Type': encType }
1589
- : {}),
1655
+ ...(encType &&
1656
+ encType !== 'multipart/form-data' && {
1657
+ 'Content-Type': encType,
1658
+ }),
1590
1659
  },
1591
1660
  body: shouldStringifySubmissionData ? formDataJson : formData,
1592
1661
  });
1593
- if (response &&
1594
- (validateStatus
1595
- ? !validateStatus(response.status)
1596
- : response.status < 200 || response.status >= 300)) {
1597
- hasError = true;
1598
- onError && onError({ response });
1599
- type = String(response.status);
1662
+ if (response && !validateStatus(response.status)) {
1663
+ onError({ response });
1664
+ return { type: String(response.status) };
1600
1665
  }
1601
1666
  else {
1602
- onSuccess && onSuccess({ response });
1667
+ onSuccess({ response });
1603
1668
  }
1604
1669
  }
1605
1670
  catch (error) {
1606
- hasError = true;
1607
- onError && onError({ error });
1671
+ onError({ error });
1672
+ return { type: '' };
1608
1673
  }
1609
1674
  }
1610
- })(event);
1611
- if (hasError && control) {
1612
- control._subjects.state.next({
1613
- isSubmitSuccessful: false,
1614
- });
1615
- control.setError('root.server', {
1616
- type,
1617
- });
1618
- }
1675
+ if (isFunction(action)) {
1676
+ try {
1677
+ await action(formData);
1678
+ }
1679
+ catch (error) {
1680
+ onError({ error });
1681
+ return { type: '' };
1682
+ }
1683
+ }
1684
+ // Return nothing when successful.
1685
+ return;
1686
+ });
1619
1687
  }, [
1620
1688
  control,
1621
1689
  onSubmit,
@@ -1627,15 +1695,28 @@ function Form(props) {
1627
1695
  onError,
1628
1696
  onSuccess,
1629
1697
  ]);
1698
+ const submit = React.useCallback(async (event) => {
1699
+ const err = await handleSubmit(event);
1700
+ if (err && control) {
1701
+ control._subjects.state.next({ isSubmitSuccessful: false });
1702
+ control.setError('root.server', err);
1703
+ }
1704
+ }, [handleSubmit, control]);
1630
1705
  React.useEffect(() => {
1631
1706
  setMounted(true);
1632
1707
  }, []);
1633
- return render ? (React.createElement(React.Fragment, null, render({
1634
- submit,
1635
- }))) : (React.createElement("form", { noValidate: mounted, action: action, method: method, encType: encType, onSubmit: submit, ...rest }, children));
1708
+ if (render) {
1709
+ return render({ submit });
1710
+ }
1711
+ // React forbids passing `method`/`encType` alongside a function `action`
1712
+ // (a Server-Action-style submission) -- it manages those itself and warns
1713
+ // if they're present, so they're only rendered for string/URL actions.
1714
+ return (React.createElement("form", { noValidate: mounted, action: action, ...(!isFunction(action) && { method, encType }), onSubmit: submit, ...rest }, children));
1636
1715
  }
1637
1716
 
1638
- const FormStateSubscribe = ({ control, disabled, exact, name, render, }) => render(useFormState({ control, name, disabled, exact }));
1717
+ const FormState = ({ control, disabled, exact, name, render, }) => render(useFormState({ control, name, disabled, exact }));
1718
+ /** @deprecated Use `FormState` instead. Kept as an alias for backward compatibility. */
1719
+ const FormStateSubscribe = FormState;
1639
1720
 
1640
1721
  var createSubject = () => {
1641
1722
  let _observers = [];
@@ -1691,6 +1772,24 @@ var isRadioOrCheckbox = (ref) => isRadioInput(ref) || isCheckBoxInput(ref);
1691
1772
 
1692
1773
  var live = (ref) => isHTMLElement(ref) && ref.isConnected;
1693
1774
 
1775
+ function isDirtyContainer(value) {
1776
+ return Array.isArray(value) || isObject(value);
1777
+ }
1778
+ function collectDirtyFieldNames(dirtyTree, cachedDirtyFields, prefix = '', names = []) {
1779
+ for (const key in dirtyTree) {
1780
+ const path = prefix ? `${prefix}.${key}` : key;
1781
+ const value = dirtyTree[key];
1782
+ if (isDirtyContainer(value) &&
1783
+ isDirtyContainer(get(cachedDirtyFields, path))) {
1784
+ collectDirtyFieldNames(value, cachedDirtyFields, path, names);
1785
+ }
1786
+ else {
1787
+ names.push(path);
1788
+ }
1789
+ }
1790
+ return names;
1791
+ }
1792
+
1694
1793
  var objectHasFunction = (data) => {
1695
1794
  for (const key in data) {
1696
1795
  if (isFunction(data[key])) {
@@ -1794,7 +1893,7 @@ function getFieldValue(_f) {
1794
1893
  if (isCheckBoxInput(ref)) {
1795
1894
  return getCheckboxValue(_f.refs).value;
1796
1895
  }
1797
- return getFieldValueAs(isUndefined(ref.value) ? _f.ref.value : ref.value, _f);
1896
+ return getFieldValueAs(ref.value, _f);
1798
1897
  }
1799
1898
 
1800
1899
  var getResolverOptions = (fieldsNames, _fields, criteriaMode, shouldUseNativeValidation) => {
@@ -1885,8 +1984,7 @@ function schemaErrorLookup(errors, _fields, name) {
1885
1984
 
1886
1985
  var shouldRenderFormState = (formStateData, _proxyFormState, updateFormState, isRoot) => {
1887
1986
  updateFormState(formStateData);
1888
- const { name, ...formState } = formStateData;
1889
- const keys = Object.keys(formState);
1987
+ const keys = Object.keys(formStateData).filter((key) => key !== 'name');
1890
1988
  return (!keys.length ||
1891
1989
  (isRoot && keys.length >= Object.keys(_proxyFormState).length) ||
1892
1990
  keys.find((key) => _proxyFormState[key] ===
@@ -1918,7 +2016,12 @@ var skipValidation = (isBlurEvent, isTouched, isSubmitted, reValidateMode, mode)
1918
2016
  return true;
1919
2017
  };
1920
2018
 
1921
- var unsetEmptyArray = (ref, name) => !compact(get(ref, name)).length && unset(ref, name);
2019
+ var unsetEmptyArray = (ref, name) => {
2020
+ const array = get(ref, name);
2021
+ !compact(array).length &&
2022
+ !(array === null || array === void 0 ? void 0 : array.root) &&
2023
+ unset(ref, name);
2024
+ };
1922
2025
 
1923
2026
  const defaultOptions = {
1924
2027
  mode: VALIDATION_MODE.onSubmit,
@@ -1967,6 +2070,7 @@ function createFormControl(props = {}) {
1967
2070
  : cloneObject(_defaultValues);
1968
2071
  let _state = {
1969
2072
  action: false,
2073
+ actionArrayLengths: new Map(),
1970
2074
  mount: false,
1971
2075
  watch: false,
1972
2076
  keepIsValid: false,
@@ -2062,13 +2166,22 @@ function createFormControl(props = {}) {
2062
2166
  const _setFieldArray = (name, values = [], method, args, shouldSetValues = true, shouldUpdateFieldsAndState = true) => {
2063
2167
  if (args && method && !_options.disabled) {
2064
2168
  _state.action = true;
2169
+ if (!_state.actionArrayLengths.has(name)) {
2170
+ const preActionFields = get(_fields, name);
2171
+ _state.actionArrayLengths.set(name, Array.isArray(preActionFields) ? preActionFields.length : 0);
2172
+ }
2065
2173
  if (shouldUpdateFieldsAndState && Array.isArray(get(_fields, name))) {
2066
2174
  const fieldValues = method(get(_fields, name), args.argA, args.argB);
2067
2175
  shouldSetValues && set(_fields, name, fieldValues);
2068
2176
  }
2069
2177
  if (shouldUpdateFieldsAndState &&
2070
2178
  Array.isArray(get(_formState.errors, name))) {
2071
- const errors = method(get(_formState.errors, name), args.argA, args.argB);
2179
+ const fieldArrayErrors = get(_formState.errors, name);
2180
+ const rootError = fieldArrayErrors.root;
2181
+ const errors = method(fieldArrayErrors, args.argA, args.argB) || fieldArrayErrors;
2182
+ if (rootError) {
2183
+ errors.root = rootError;
2184
+ }
2072
2185
  shouldSetValues && set(_formState.errors, name, errors);
2073
2186
  unsetEmptyArray(_formState.errors, name);
2074
2187
  }
@@ -2124,10 +2237,40 @@ function createFormControl(props = {}) {
2124
2237
  }
2125
2238
  return false;
2126
2239
  };
2240
+ const isStaleArrayIndex = (name) => {
2241
+ if (!_state.actionArrayLengths.size) {
2242
+ return false;
2243
+ }
2244
+ const segments = isKey(name) ? [name] : stringToPath(name);
2245
+ let node = _formValues;
2246
+ let path = '';
2247
+ let ownerDepth = -1;
2248
+ let ownerPreActionLength = 0;
2249
+ for (let i = 0; i < segments.length; i++) {
2250
+ if (isNullOrUndefined(node)) {
2251
+ return false;
2252
+ }
2253
+ const key = segments[i];
2254
+ path = path ? `${path}.${key}` : key;
2255
+ if (Array.isArray(node) && +key >= node.length) {
2256
+ return ownerDepth === -1
2257
+ ? false
2258
+ : i === ownerDepth
2259
+ ? +key < ownerPreActionLength
2260
+ : true;
2261
+ }
2262
+ if (_state.actionArrayLengths.has(path)) {
2263
+ ownerDepth = i + 1;
2264
+ ownerPreActionLength = _state.actionArrayLengths.get(path);
2265
+ }
2266
+ node = node[key];
2267
+ }
2268
+ return false;
2269
+ };
2127
2270
  const updateValidAndValue = (name, shouldSkipSetValueAs, value, ref) => {
2128
2271
  const field = get(_fields, name);
2129
2272
  if (field) {
2130
- if (hasExplicitNullIntermediate(name)) {
2273
+ if (hasExplicitNullIntermediate(name) || isStaleArrayIndex(name)) {
2131
2274
  return;
2132
2275
  }
2133
2276
  const wasUnsetInFormValues = isUndefined(get(_formValues, name));
@@ -2375,14 +2518,12 @@ function createFormControl(props = {}) {
2375
2518
  const _getWatch = (names, defaultValue, isGlobal) => generateWatchOutput(names, _names, {
2376
2519
  ...(_state.mount
2377
2520
  ? _formValues
2378
- : isUndefined(defaultValue)
2521
+ : isUndefined(defaultValue) || isString(names)
2379
2522
  ? _defaultValues
2380
- : isString(names)
2381
- ? { [names]: defaultValue }
2382
- : defaultValue),
2523
+ : defaultValue),
2383
2524
  }, isGlobal, defaultValue);
2384
2525
  const _getFieldArray = (name) => compact(get(_state.mount ? _formValues : _defaultValues, name, _options.shouldUnregister ? get(_defaultValues, name, []) : []));
2385
- const setFieldValue = (name, value, options = {}, skipClone = false, skipRender = false) => {
2526
+ const setFieldValue = (name, value, options = {}, skipClone = false, skipRender = false, skipValueRender = false) => {
2386
2527
  const field = get(_fields, name);
2387
2528
  let fieldValue = value;
2388
2529
  if (field) {
@@ -2420,7 +2561,7 @@ function createFormControl(props = {}) {
2420
2561
  }
2421
2562
  else {
2422
2563
  fieldReference.ref.value = fieldValue;
2423
- if (!fieldReference.ref.type && !skipRender) {
2564
+ if (!fieldReference.ref.type && !skipRender && !skipValueRender) {
2424
2565
  _subjects.state.next({
2425
2566
  name,
2426
2567
  values: skipClone ? _formValues : cloneObject(_formValues),
@@ -2436,7 +2577,13 @@ function createFormControl(props = {}) {
2436
2577
  delayError: options.delayError,
2437
2578
  });
2438
2579
  };
2439
- const setFieldValues = (name, value, options, skipClone = false, skipRender = false) => {
2580
+ const setFieldValues = (name, value, options, skipClone = false, skipRender = false, skipValueRender = false) => {
2581
+ if (_names.array.has(name)) {
2582
+ _subjects.array.next({
2583
+ name,
2584
+ values: skipClone ? _formValues : cloneObject(_formValues),
2585
+ });
2586
+ }
2440
2587
  for (const fieldKey in value) {
2441
2588
  if (!value.hasOwnProperty(fieldKey)) {
2442
2589
  return;
@@ -2448,8 +2595,8 @@ function createFormControl(props = {}) {
2448
2595
  isObject(fieldValue) ||
2449
2596
  (field && !field._f)) &&
2450
2597
  !isDateObject(fieldValue)
2451
- ? setFieldValues(fieldName, fieldValue, options, skipClone, skipRender)
2452
- : setFieldValue(fieldName, fieldValue, options, skipClone, skipRender);
2598
+ ? setFieldValues(fieldName, fieldValue, options, skipClone, skipRender, skipValueRender)
2599
+ : setFieldValue(fieldName, fieldValue, options, skipClone, skipRender, skipValueRender);
2453
2600
  }
2454
2601
  };
2455
2602
  const _setValue = (name, value, options, skipClone, skipStateEmit = false) => {
@@ -2484,11 +2631,12 @@ function createFormControl(props = {}) {
2484
2631
  else {
2485
2632
  const isEmpty = (Array.isArray(cloneValue) && !cloneValue.length) ||
2486
2633
  isEmptyObject(cloneValue);
2634
+ const skipValueRender = !isValueUnchanged && !skipStateEmit;
2487
2635
  if (!field || field._f || isNullOrUndefined(cloneValue) || isEmpty) {
2488
- setFieldValue(name, cloneValue, options, skipClone, skipStateEmit);
2636
+ setFieldValue(name, cloneValue, options, skipClone, skipStateEmit, skipValueRender);
2489
2637
  }
2490
2638
  else {
2491
- setFieldValues(name, cloneValue, options, skipClone, skipStateEmit);
2639
+ setFieldValues(name, cloneValue, options, skipClone, skipStateEmit, skipValueRender);
2492
2640
  }
2493
2641
  }
2494
2642
  if (!isValueUnchanged && !skipStateEmit) {
@@ -2972,6 +3120,7 @@ function createFormControl(props = {}) {
2972
3120
  }
2973
3121
  };
2974
3122
  const handleSubmit = (onValid, onInvalid) => async (e) => {
3123
+ let result = undefined;
2975
3124
  let onValidError = undefined;
2976
3125
  if (e) {
2977
3126
  e.preventDefault && e.preventDefault();
@@ -3005,7 +3154,7 @@ function createFormControl(props = {}) {
3005
3154
  errors: {},
3006
3155
  });
3007
3156
  try {
3008
- await onValid(fieldValues, e);
3157
+ result = await onValid(fieldValues, e);
3009
3158
  }
3010
3159
  catch (error) {
3011
3160
  onValidError = error;
@@ -3028,6 +3177,7 @@ function createFormControl(props = {}) {
3028
3177
  if (onValidError) {
3029
3178
  throw onValidError;
3030
3179
  }
3180
+ return result;
3031
3181
  };
3032
3182
  const resetField = (name, options = {}) => {
3033
3183
  if (get(_fields, name)) {
@@ -3067,7 +3217,7 @@ function createFormControl(props = {}) {
3067
3217
  if (keepStateOptions.keepDirtyValues) {
3068
3218
  const fieldsToCheck = new Set([
3069
3219
  ..._names.mount,
3070
- ...Object.keys(getDirtyFields(_defaultValues, _formValues, undefined, fieldRefs)),
3220
+ ...collectDirtyFieldNames(getDirtyFields(_defaultValues, _formValues, undefined, fieldRefs), _formState.dirtyFields),
3071
3221
  ]);
3072
3222
  for (const fieldName of Array.from(fieldsToCheck)) {
3073
3223
  const isDirty = get(_formState.dirtyFields, fieldName);
@@ -3148,6 +3298,7 @@ function createFormControl(props = {}) {
3148
3298
  _state.watch = !!_options.shouldUnregister;
3149
3299
  _state.keepIsValid = !!keepStateOptions.keepIsValid;
3150
3300
  _state.action = false;
3301
+ _state.actionArrayLengths.clear();
3151
3302
  if (!keepStateOptions.keepErrors) {
3152
3303
  _formState.errors = {};
3153
3304
  }
@@ -3220,6 +3371,7 @@ function createFormControl(props = {}) {
3220
3371
  ...formState,
3221
3372
  };
3222
3373
  };
3374
+ _subjects.state.subscribe({ next: _setFormState });
3223
3375
  const _resetDefaultValues = () => isFunction(_options.defaultValues) &&
3224
3376
  _options.defaultValues().then((values) => {
3225
3377
  reset(values, _options.resetOptions);
@@ -3389,8 +3541,14 @@ function useForm(props = {}) {
3389
3541
  }
3390
3542
  const control = _formControl.current.control;
3391
3543
  control._options = props;
3544
+ const { resyncIfNeeded, snapshot } = useResyncOnReconnect();
3392
3545
  useIsomorphicLayoutEffect(() => {
3393
- const sub = control._subscribe({
3546
+ const getCurrentFormState = () => ({
3547
+ ...control._formState,
3548
+ defaultValues: control._defaultValues,
3549
+ });
3550
+ resyncIfNeeded(true, getCurrentFormState, updateFormState);
3551
+ const unsubscribe = control._subscribe({
3394
3552
  formState: control._proxyFormState,
3395
3553
  callback: () => updateFormState({
3396
3554
  ...control._formState,
@@ -3403,8 +3561,11 @@ function useForm(props = {}) {
3403
3561
  isReady: true,
3404
3562
  }));
3405
3563
  control._formState.isReady = true;
3406
- return sub;
3407
- }, [control]);
3564
+ return () => {
3565
+ unsubscribe();
3566
+ snapshot(true, getCurrentFormState);
3567
+ };
3568
+ }, [control, resyncIfNeeded, snapshot]);
3408
3569
  React.useEffect(() => control._disableForm(props.disabled), [control, props.disabled]);
3409
3570
  React.useEffect(() => {
3410
3571
  if (props.mode) {
@@ -3496,5 +3657,5 @@ function useForm(props = {}) {
3496
3657
  */
3497
3658
  const Watch = (props) => props.render(useWatch({ name: props.names, ...props }));
3498
3659
 
3499
- export { Controller, FieldArray, Form, FormProvider, FormStateSubscribe, Watch, appendErrors, createFormControl, get, set, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch };
3660
+ export { Controller, FieldArray, Form, FormProvider, FormState, FormStateSubscribe, Watch, appendErrors, createFormControl, get, set, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch };
3500
3661
  //# sourceMappingURL=index.esm.mjs.map