react-hook-form 7.18.0-next.0 → 7.18.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.
package/dist/index.esm.js CHANGED
@@ -101,7 +101,13 @@ var shouldRenderFormState = (formStateData, _proxyFormState, isRoot) => {
101
101
  (!isRoot || VALIDATION_MODE.all)));
102
102
  };
103
103
 
104
- var convertToArrayPayload = (value) => Array.isArray(value) ? value : [value];
104
+ var convertToArrayPayload = (value) => (Array.isArray(value) ? value : [value]);
105
+
106
+ var shouldSubscribeByName = (name, signalName) => !name ||
107
+ !signalName ||
108
+ convertToArrayPayload(name).some((currentName) => currentName &&
109
+ (currentName.startsWith(signalName) ||
110
+ signalName.startsWith(currentName)));
105
111
 
106
112
  const tearDown = (_unsubscribe) => {
107
113
  if (_unsubscribe.current) {
@@ -149,12 +155,11 @@ function useFormState(props) {
149
155
  _name.current = name;
150
156
  useSubscribe({
151
157
  disabled,
152
- callback: (formState) => (!_name.current ||
153
- !formState.name ||
154
- convertToArrayPayload(_name.current).includes(formState.name)) &&
158
+ callback: (formState) => shouldSubscribeByName(_name.current, formState.name) &&
155
159
  shouldRenderFormState(formState, _localProxyFormState.current) &&
156
160
  updateFormState(Object.assign(Object.assign({}, control._formState), formState)),
157
161
  subject: control._subjects.state,
162
+ skipEarlySubscription: true,
158
163
  });
159
164
  return getProxyFormState(formState, control._proxyFormState, _localProxyFormState.current, false);
160
165
  }
@@ -173,6 +178,7 @@ function useController(props) {
173
178
  subject: control._subjects.control,
174
179
  callback: (data) => (!data.name || _name.current === data.name) &&
175
180
  setInputStateValue(get(data.values, _name.current)),
181
+ skipEarlySubscription: true,
176
182
  });
177
183
  const registerProps = control.register(name, Object.assign(Object.assign({}, props.rules), { value }));
178
184
  const updateMounted = React.useCallback((name, value) => {
@@ -462,6 +468,15 @@ const useFieldArray = (props) => {
462
468
  }
463
469
  }
464
470
  }
471
+ control._executeSchema([name]).then((result) => {
472
+ const error = get(result.errors, name);
473
+ if (error && error.type && !get(control._formState.errors, name)) {
474
+ set(control._formState.errors, name, error);
475
+ control._subjects.state.next({
476
+ errors: control._formState.errors,
477
+ });
478
+ }
479
+ });
465
480
  control._subjects.watch.next({
466
481
  name,
467
482
  values: control._formValues,
@@ -519,6 +534,60 @@ function cloneObject(data) {
519
534
  return copy;
520
535
  }
521
536
 
537
+ function createSubscription() {
538
+ let tearDowns = [];
539
+ const add = (tearDown) => {
540
+ tearDowns.push(tearDown);
541
+ };
542
+ const unsubscribe = () => {
543
+ for (const teardown of tearDowns) {
544
+ teardown();
545
+ }
546
+ tearDowns = [];
547
+ };
548
+ return {
549
+ add,
550
+ unsubscribe,
551
+ };
552
+ }
553
+ function createSubscriber(observer, subscription) {
554
+ let closed = false;
555
+ subscription.add(() => (closed = true));
556
+ const next = (value) => {
557
+ if (!closed) {
558
+ observer.next(value);
559
+ }
560
+ };
561
+ return {
562
+ next,
563
+ };
564
+ }
565
+ function createSubject() {
566
+ let _observers = [];
567
+ const next = (value) => {
568
+ for (const observer of _observers) {
569
+ observer.next(value);
570
+ }
571
+ };
572
+ const subscribe = (observer) => {
573
+ const subscription = createSubscription();
574
+ const subscriber = createSubscriber(observer, subscription);
575
+ _observers.push(subscriber);
576
+ return subscription;
577
+ };
578
+ const unsubscribe = () => {
579
+ _observers = [];
580
+ };
581
+ return {
582
+ get observers() {
583
+ return _observers;
584
+ },
585
+ next,
586
+ subscribe,
587
+ unsubscribe,
588
+ };
589
+ }
590
+
522
591
  var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
523
592
 
524
593
  function deepEqual(object1, object2) {
@@ -562,6 +631,8 @@ var getValidationModes = (mode) => ({
562
631
 
563
632
  var isBoolean = (value) => typeof value === 'boolean';
564
633
 
634
+ var isFileInput = (element) => element.type === 'file';
635
+
565
636
  var isHTMLElement = (value) => value instanceof HTMLElement;
566
637
 
567
638
  var isMultipleSelect = (element) => element.type === `select-multiple`;
@@ -576,53 +647,7 @@ var isWeb = typeof window !== 'undefined' &&
576
647
  typeof window.HTMLElement !== 'undefined' &&
577
648
  typeof document !== 'undefined';
578
649
 
579
- var live = (ref) => !isHTMLElement(ref) || !document.contains(ref);
580
-
581
- class Subscription {
582
- constructor() {
583
- this.tearDowns = [];
584
- }
585
- add(tearDown) {
586
- this.tearDowns.push(tearDown);
587
- }
588
- unsubscribe() {
589
- for (const teardown of this.tearDowns) {
590
- teardown();
591
- }
592
- this.tearDowns = [];
593
- }
594
- }
595
- class Subscriber {
596
- constructor(observer, subscription) {
597
- this.observer = observer;
598
- this.closed = false;
599
- subscription.add(() => (this.closed = true));
600
- }
601
- next(value) {
602
- if (!this.closed) {
603
- this.observer.next(value);
604
- }
605
- }
606
- }
607
- class Subject {
608
- constructor() {
609
- this.observers = [];
610
- }
611
- next(value) {
612
- for (const observer of this.observers) {
613
- observer.next(value);
614
- }
615
- }
616
- subscribe(observer) {
617
- const subscription = new Subscription();
618
- const subscriber = new Subscriber(observer, subscription);
619
- this.observers.push(subscriber);
620
- return subscription;
621
- }
622
- unsubscribe() {
623
- this.observers = [];
624
- }
625
- }
650
+ var live = (ref) => isHTMLElement(ref) && document.contains(ref);
626
651
 
627
652
  function baseGet(object, updatePath) {
628
653
  const length = updatePath.slice(0, -1).length;
@@ -663,8 +688,6 @@ function unset(object, path) {
663
688
  return object;
664
689
  }
665
690
 
666
- var isFileInput = (element) => element.type === 'file';
667
-
668
691
  const defaultResult = {
669
692
  value: false,
670
693
  isValid: false,
@@ -758,6 +781,35 @@ var hasValidation = (options) => options.mount &&
758
781
  options.pattern ||
759
782
  options.validate);
760
783
 
784
+ function schemaErrorLookup(errors, _fields, name) {
785
+ const error = get(errors, name);
786
+ if (error || isKey(name)) {
787
+ return {
788
+ error,
789
+ name,
790
+ };
791
+ }
792
+ const names = name.split('.');
793
+ while (names.length) {
794
+ const fieldName = names.join('.');
795
+ const field = get(_fields, fieldName);
796
+ const foundError = get(errors, fieldName);
797
+ if (field && !Array.isArray(field) && name !== fieldName) {
798
+ return { name };
799
+ }
800
+ if (foundError && foundError.type) {
801
+ return {
802
+ name: fieldName,
803
+ error: foundError,
804
+ };
805
+ }
806
+ names.pop();
807
+ }
808
+ return {
809
+ name,
810
+ };
811
+ }
812
+
761
813
  function deepMerge(target, source) {
762
814
  if (isPrimitive(target) || isPrimitive(source)) {
763
815
  return source;
@@ -1027,10 +1079,10 @@ function createFormControl(props = {}) {
1027
1079
  errors: false,
1028
1080
  };
1029
1081
  const _subjects = {
1030
- watch: new Subject(),
1031
- control: new Subject(),
1032
- array: new Subject(),
1033
- state: new Subject(),
1082
+ watch: createSubject(),
1083
+ control: createSubject(),
1084
+ array: createSubject(),
1085
+ state: createSubject(),
1034
1086
  };
1035
1087
  const validationModeBeforeSubmit = getValidationModes(_options.mode);
1036
1088
  const validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
@@ -1047,7 +1099,7 @@ function createFormControl(props = {}) {
1047
1099
  let isValid = false;
1048
1100
  if (_proxyFormState.isValid) {
1049
1101
  isValid = _options.resolver
1050
- ? isEmptyObject((await executeResolver()).errors)
1102
+ ? isEmptyObject((await _executeSchema()).errors)
1051
1103
  : await executeBuildInValidation(_fields, true);
1052
1104
  if (!shouldSkipRender && isValid !== _formState.isValid) {
1053
1105
  _formState.isValid = isValid;
@@ -1166,11 +1218,11 @@ function createFormControl(props = {}) {
1166
1218
  validateFields = {};
1167
1219
  }
1168
1220
  };
1169
- const executeResolver = async (name) => _options.resolver
1221
+ const _executeSchema = async (name) => _options.resolver
1170
1222
  ? await _options.resolver(Object.assign({}, _formValues), _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation))
1171
1223
  : {};
1172
- const executeResolverValidation = async (names) => {
1173
- const { errors } = await executeResolver();
1224
+ const executeSchemaAndUpdateState = async (names) => {
1225
+ const { errors } = await _executeSchema();
1174
1226
  if (names) {
1175
1227
  for (const name of names) {
1176
1228
  const error = get(errors, name);
@@ -1216,7 +1268,9 @@ function createFormControl(props = {}) {
1216
1268
  for (const name of _names.unMount) {
1217
1269
  const field = get(_fields, name);
1218
1270
  field &&
1219
- (field._f.refs ? field._f.refs.every(live) : live(field._f.ref)) &&
1271
+ (field._f.refs
1272
+ ? field._f.refs.every((ref) => !live(ref))
1273
+ : !live(field._f.ref)) &&
1220
1274
  unregister(name);
1221
1275
  }
1222
1276
  _names.unMount = new Set();
@@ -1239,7 +1293,7 @@ function createFormControl(props = {}) {
1239
1293
  isGlobal && (_names.watchAll = true);
1240
1294
  return fieldValues;
1241
1295
  };
1242
- const _getFieldArray = (name) => get(_stateFlags.mount ? _formValues : _defaultValues, name, []);
1296
+ const _getFieldArray = (name) => get(_stateFlags.mount ? _formValues : _defaultValues, name, props.shouldUnregister ? get(_defaultValues, name, []) : []);
1243
1297
  const setFieldValue = (name, value, options = {}, shouldRender) => {
1244
1298
  const field = get(_fields, name);
1245
1299
  let fieldValue = value;
@@ -1251,7 +1305,10 @@ function createFormControl(props = {}) {
1251
1305
  isWeb && isHTMLElement(fieldReference.ref) && isNullOrUndefined(value)
1252
1306
  ? ''
1253
1307
  : value;
1254
- if (isMultipleSelect(fieldReference.ref)) {
1308
+ if (isFileInput(fieldReference.ref) && !isString(fieldValue)) {
1309
+ fieldReference.ref.files = fieldValue;
1310
+ }
1311
+ else if (isMultipleSelect(fieldReference.ref)) {
1255
1312
  [...fieldReference.ref.options].forEach((selectRef) => (selectRef.selected = fieldValue.includes(selectRef.value)));
1256
1313
  }
1257
1314
  else if (fieldReference.refs) {
@@ -1362,18 +1419,11 @@ function createFormControl(props = {}) {
1362
1419
  isValidating: true,
1363
1420
  });
1364
1421
  if (_options.resolver) {
1365
- const { errors } = await executeResolver([name]);
1366
- error = get(errors, name);
1367
- if (isCheckBoxInput(target) && !error) {
1368
- const parentNodeName = getNodeParentName(name);
1369
- const parentField = get(_fields, parentNodeName);
1370
- if (Array.isArray(parentField) &&
1371
- parentField.every((field) => field._f && isCheckBoxInput(field._f.ref))) {
1372
- const parentError = get(errors, parentNodeName, {});
1373
- parentError.type && (error = parentError);
1374
- name = parentNodeName;
1375
- }
1376
- }
1422
+ const { errors } = await _executeSchema([name]);
1423
+ const previousErrorLookupResult = schemaErrorLookup(_formState.errors, _fields, name);
1424
+ const errorLookupResult = schemaErrorLookup(errors, _fields, previousErrorLookupResult.name || name);
1425
+ error = errorLookupResult.error;
1426
+ name = errorLookupResult.name;
1377
1427
  isValid = isEmptyObject(errors);
1378
1428
  }
1379
1429
  else {
@@ -1392,7 +1442,7 @@ function createFormControl(props = {}) {
1392
1442
  isValidating: true,
1393
1443
  });
1394
1444
  if (_options.resolver) {
1395
- const errors = await executeResolverValidation(isUndefined(name) ? name : fieldNames);
1445
+ const errors = await executeSchemaAndUpdateState(isUndefined(name) ? name : fieldNames);
1396
1446
  isValid = isEmptyObject(errors);
1397
1447
  validationResult = name
1398
1448
  ? !fieldNames.some((name) => get(errors, name))
@@ -1408,7 +1458,10 @@ function createFormControl(props = {}) {
1408
1458
  else {
1409
1459
  validationResult = isValid = await executeBuildInValidation(_fields);
1410
1460
  }
1411
- _subjects.state.next(Object.assign(Object.assign({}, (!isString(name) || isValid !== _formState.isValid ? {} : { name })), { errors: _formState.errors, isValid, isValidating: false }));
1461
+ _subjects.state.next(Object.assign(Object.assign(Object.assign({}, (!isString(name) ||
1462
+ (_proxyFormState.isValid && isValid !== _formState.isValid)
1463
+ ? {}
1464
+ : { name })), (_options.resolver ? { isValid } : {})), { errors: _formState.errors, isValidating: false }));
1412
1465
  options.shouldFocus &&
1413
1466
  !validationResult &&
1414
1467
  focusFieldBy(_fields, (key) => get(_formState.errors, key), name ? fieldNames : _names.mount);
@@ -1428,6 +1481,7 @@ function createFormControl(props = {}) {
1428
1481
  : (_formState.errors = {});
1429
1482
  _subjects.state.next({
1430
1483
  errors: _formState.errors,
1484
+ isValid: true,
1431
1485
  });
1432
1486
  };
1433
1487
  const setError = (name, error, options) => {
@@ -1502,7 +1556,7 @@ function createFormControl(props = {}) {
1502
1556
  field = {
1503
1557
  _f: isRadioOrCheckbox
1504
1558
  ? Object.assign(Object.assign({}, field._f), { refs: [
1505
- ...compact(field._f.refs || []).filter((ref) => isHTMLElement(ref) && document.contains(ref)),
1559
+ ...compact(field._f.refs || []).filter(live),
1506
1560
  fieldRef,
1507
1561
  ], ref: { type: fieldRef.type, name } }) : Object.assign(Object.assign({}, field._f), { ref: fieldRef }),
1508
1562
  };
@@ -1534,7 +1588,7 @@ function createFormControl(props = {}) {
1534
1588
  });
1535
1589
  try {
1536
1590
  if (_options.resolver) {
1537
- const { errors, values } = await executeResolver();
1591
+ const { errors, values } = await _executeSchema();
1538
1592
  _formState.errors = errors;
1539
1593
  fieldValues = values;
1540
1594
  }
@@ -1594,7 +1648,11 @@ function createFormControl(props = {}) {
1594
1648
  }
1595
1649
  }
1596
1650
  }
1597
- _formValues = props.shouldUnregister ? {} : cloneUpdatedValues;
1651
+ _formValues = props.shouldUnregister
1652
+ ? keepStateOptions.keepDefaultValues
1653
+ ? cloneObject(_defaultValues)
1654
+ : {}
1655
+ : cloneUpdatedValues;
1598
1656
  _fields = {};
1599
1657
  _subjects.control.next({
1600
1658
  values: hasUpdatedFormValues ? cloneUpdatedValues : _defaultValues,
@@ -1619,14 +1677,16 @@ function createFormControl(props = {}) {
1619
1677
  isDirty: keepStateOptions.keepDirty
1620
1678
  ? _formState.isDirty
1621
1679
  : keepStateOptions.keepDefaultValues
1622
- ? deepEqual(formValues, _defaultValues)
1680
+ ? !deepEqual(formValues, _defaultValues)
1623
1681
  : false,
1624
1682
  isSubmitted: keepStateOptions.keepIsSubmitted
1625
1683
  ? _formState.isSubmitted
1626
1684
  : false,
1627
1685
  dirtyFields: keepStateOptions.keepDirty
1628
1686
  ? _formState.dirtyFields
1629
- : {},
1687
+ : (keepStateOptions.keepDefaultValues && formValues
1688
+ ? Object.entries(formValues).reduce((previous, [key, value]) => (Object.assign(Object.assign({}, previous), { [key]: value !== get(_defaultValues, key) })), {})
1689
+ : {}),
1630
1690
  touchedFields: keepStateOptions.keepTouched
1631
1691
  ? _formState.touchedFields
1632
1692
  : {},
@@ -1640,11 +1700,15 @@ function createFormControl(props = {}) {
1640
1700
  !_proxyFormState.isValid || !!keepStateOptions.keepIsValid;
1641
1701
  _stateFlags.watch = !!props.shouldUnregister;
1642
1702
  };
1643
- const setFocus = (name) => get(_fields, name)._f.ref.focus();
1703
+ const setFocus = (name) => {
1704
+ const field = get(_fields, name)._f;
1705
+ (field.ref.focus ? field.ref : field.refs[0]).focus();
1706
+ };
1644
1707
  return {
1645
1708
  control: {
1646
1709
  register,
1647
1710
  unregister,
1711
+ _executeSchema,
1648
1712
  _getWatch,
1649
1713
  _getDirty,
1650
1714
  _updateValid,
@@ -1751,6 +1815,7 @@ function useForm(props = {}) {
1751
1815
  }
1752
1816
  control._removeUnmounted();
1753
1817
  });
1818
+ React.useEffect(() => () => Object.values(control._subjects).forEach((subject) => subject.unsubscribe()), [control]);
1754
1819
  _formControl.current.formState = getProxyFormState(formState, control._proxyFormState);
1755
1820
  return _formControl.current;
1756
1821
  }
@@ -1763,13 +1828,8 @@ function useWatch(props) {
1763
1828
  useSubscribe({
1764
1829
  disabled,
1765
1830
  subject: control._subjects.watch,
1766
- callback: ({ name }) => {
1767
- if (!_name.current ||
1768
- !name ||
1769
- convertToArrayPayload(_name.current).some((currentName) => name &&
1770
- currentName &&
1771
- (name.startsWith(currentName) ||
1772
- currentName.startsWith(name)))) {
1831
+ callback: (formState) => {
1832
+ if (shouldSubscribeByName(_name.current, formState.name)) {
1773
1833
  control._stateFlags.mount = true;
1774
1834
  const fieldValues = control._getWatch(_name.current, defaultValue);
1775
1835
  updateValue(isObject(fieldValues)