react-hook-form 7.17.4 → 7.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -159,6 +159,7 @@ function useFormState(props) {
159
159
  shouldRenderFormState(formState, _localProxyFormState.current) &&
160
160
  updateFormState(Object.assign(Object.assign({}, control._formState), formState)),
161
161
  subject: control._subjects.state,
162
+ skipEarlySubscription: true,
162
163
  });
163
164
  return getProxyFormState(formState, control._proxyFormState, _localProxyFormState.current, false);
164
165
  }
@@ -177,6 +178,7 @@ function useController(props) {
177
178
  subject: control._subjects.control,
178
179
  callback: (data) => (!data.name || _name.current === data.name) &&
179
180
  setInputStateValue(get(data.values, _name.current)),
181
+ skipEarlySubscription: true,
180
182
  });
181
183
  const registerProps = control.register(name, Object.assign(Object.assign({}, props.rules), { value }));
182
184
  const updateMounted = React.useCallback((name, value) => {
@@ -377,6 +379,7 @@ const useFieldArray = (props) => {
377
379
  const [fields, setFields] = React.useState(mapIds(control._getFieldArray(name), keyName));
378
380
  const _fieldIds = React.useRef(fields);
379
381
  const _name = React.useRef(name);
382
+ const _actioned = React.useRef(false);
380
383
  _name.current = name;
381
384
  _fieldIds.current = fields;
382
385
  control._names.array.add(name);
@@ -391,6 +394,7 @@ const useFieldArray = (props) => {
391
394
  });
392
395
  const updateValues = React.useCallback((updatedFieldArrayValuesWithKey) => {
393
396
  const updatedFieldArrayValues = omitKeys(updatedFieldArrayValuesWithKey, keyName);
397
+ _actioned.current = true;
394
398
  set(control._formValues, name, updatedFieldArrayValues);
395
399
  setFields(updatedFieldArrayValuesWithKey);
396
400
  return updatedFieldArrayValues;
@@ -466,6 +470,17 @@ const useFieldArray = (props) => {
466
470
  }
467
471
  }
468
472
  }
473
+ if (_actioned.current) {
474
+ control._executeSchema([name]).then((result) => {
475
+ const error = get(result.errors, name);
476
+ if (error && error.type && !get(control._formState.errors, name)) {
477
+ set(control._formState.errors, name, error);
478
+ control._subjects.state.next({
479
+ errors: control._formState.errors,
480
+ });
481
+ }
482
+ });
483
+ }
469
484
  control._subjects.watch.next({
470
485
  name,
471
486
  values: control._formValues,
@@ -523,6 +538,60 @@ function cloneObject(data) {
523
538
  return copy;
524
539
  }
525
540
 
541
+ function createSubscription() {
542
+ let tearDowns = [];
543
+ const add = (tearDown) => {
544
+ tearDowns.push(tearDown);
545
+ };
546
+ const unsubscribe = () => {
547
+ for (const teardown of tearDowns) {
548
+ teardown();
549
+ }
550
+ tearDowns = [];
551
+ };
552
+ return {
553
+ add,
554
+ unsubscribe,
555
+ };
556
+ }
557
+ function createSubscriber(observer, subscription) {
558
+ let closed = false;
559
+ subscription.add(() => (closed = true));
560
+ const next = (value) => {
561
+ if (!closed) {
562
+ observer.next(value);
563
+ }
564
+ };
565
+ return {
566
+ next,
567
+ };
568
+ }
569
+ function createSubject() {
570
+ let _observers = [];
571
+ const next = (value) => {
572
+ for (const observer of _observers) {
573
+ observer.next(value);
574
+ }
575
+ };
576
+ const subscribe = (observer) => {
577
+ const subscription = createSubscription();
578
+ const subscriber = createSubscriber(observer, subscription);
579
+ _observers.push(subscriber);
580
+ return subscription;
581
+ };
582
+ const unsubscribe = () => {
583
+ _observers = [];
584
+ };
585
+ return {
586
+ get observers() {
587
+ return _observers;
588
+ },
589
+ next,
590
+ subscribe,
591
+ unsubscribe,
592
+ };
593
+ }
594
+
526
595
  var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
527
596
 
528
597
  function deepEqual(object1, object2) {
@@ -566,6 +635,8 @@ var getValidationModes = (mode) => ({
566
635
 
567
636
  var isBoolean = (value) => typeof value === 'boolean';
568
637
 
638
+ var isFileInput = (element) => element.type === 'file';
639
+
569
640
  var isHTMLElement = (value) => value instanceof HTMLElement;
570
641
 
571
642
  var isMultipleSelect = (element) => element.type === `select-multiple`;
@@ -582,52 +653,6 @@ var isWeb = typeof window !== 'undefined' &&
582
653
 
583
654
  var live = (ref) => isHTMLElement(ref) && document.contains(ref);
584
655
 
585
- class Subscription {
586
- constructor() {
587
- this.tearDowns = [];
588
- }
589
- add(tearDown) {
590
- this.tearDowns.push(tearDown);
591
- }
592
- unsubscribe() {
593
- for (const teardown of this.tearDowns) {
594
- teardown();
595
- }
596
- this.tearDowns = [];
597
- }
598
- }
599
- class Subscriber {
600
- constructor(observer, subscription) {
601
- this.observer = observer;
602
- this.closed = false;
603
- subscription.add(() => (this.closed = true));
604
- }
605
- next(value) {
606
- if (!this.closed) {
607
- this.observer.next(value);
608
- }
609
- }
610
- }
611
- class Subject {
612
- constructor() {
613
- this.observers = [];
614
- }
615
- next(value) {
616
- for (const observer of this.observers) {
617
- observer.next(value);
618
- }
619
- }
620
- subscribe(observer) {
621
- const subscription = new Subscription();
622
- const subscriber = new Subscriber(observer, subscription);
623
- this.observers.push(subscriber);
624
- return subscription;
625
- }
626
- unsubscribe() {
627
- this.observers = [];
628
- }
629
- }
630
-
631
656
  function baseGet(object, updatePath) {
632
657
  const length = updatePath.slice(0, -1).length;
633
658
  let index = 0;
@@ -667,8 +692,6 @@ function unset(object, path) {
667
692
  return object;
668
693
  }
669
694
 
670
- var isFileInput = (element) => element.type === 'file';
671
-
672
695
  const defaultResult = {
673
696
  value: false,
674
697
  isValid: false,
@@ -762,6 +785,35 @@ var hasValidation = (options) => options.mount &&
762
785
  options.pattern ||
763
786
  options.validate);
764
787
 
788
+ function schemaErrorLookup(errors, _fields, name) {
789
+ const error = get(errors, name);
790
+ if (error || isKey(name)) {
791
+ return {
792
+ error,
793
+ name,
794
+ };
795
+ }
796
+ const names = name.split('.');
797
+ while (names.length) {
798
+ const fieldName = names.join('.');
799
+ const field = get(_fields, fieldName);
800
+ const foundError = get(errors, fieldName);
801
+ if (field && !Array.isArray(field) && name !== fieldName) {
802
+ return { name };
803
+ }
804
+ if (foundError && foundError.type) {
805
+ return {
806
+ name: fieldName,
807
+ error: foundError,
808
+ };
809
+ }
810
+ names.pop();
811
+ }
812
+ return {
813
+ name,
814
+ };
815
+ }
816
+
765
817
  function deepMerge(target, source) {
766
818
  if (isPrimitive(target) || isPrimitive(source)) {
767
819
  return source;
@@ -1031,10 +1083,10 @@ function createFormControl(props = {}) {
1031
1083
  errors: false,
1032
1084
  };
1033
1085
  const _subjects = {
1034
- watch: new Subject(),
1035
- control: new Subject(),
1036
- array: new Subject(),
1037
- state: new Subject(),
1086
+ watch: createSubject(),
1087
+ control: createSubject(),
1088
+ array: createSubject(),
1089
+ state: createSubject(),
1038
1090
  };
1039
1091
  const validationModeBeforeSubmit = getValidationModes(_options.mode);
1040
1092
  const validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
@@ -1051,7 +1103,7 @@ function createFormControl(props = {}) {
1051
1103
  let isValid = false;
1052
1104
  if (_proxyFormState.isValid) {
1053
1105
  isValid = _options.resolver
1054
- ? isEmptyObject((await executeResolver()).errors)
1106
+ ? isEmptyObject((await _executeSchema()).errors)
1055
1107
  : await executeBuildInValidation(_fields, true);
1056
1108
  if (!shouldSkipRender && isValid !== _formState.isValid) {
1057
1109
  _formState.isValid = isValid;
@@ -1170,11 +1222,11 @@ function createFormControl(props = {}) {
1170
1222
  validateFields = {};
1171
1223
  }
1172
1224
  };
1173
- const executeResolver = async (name) => _options.resolver
1225
+ const _executeSchema = async (name) => _options.resolver
1174
1226
  ? await _options.resolver(Object.assign({}, _formValues), _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation))
1175
1227
  : {};
1176
- const executeResolverValidation = async (names) => {
1177
- const { errors } = await executeResolver();
1228
+ const executeSchemaAndUpdateState = async (names) => {
1229
+ const { errors } = await _executeSchema();
1178
1230
  if (names) {
1179
1231
  for (const name of names) {
1180
1232
  const error = get(errors, name);
@@ -1257,7 +1309,10 @@ function createFormControl(props = {}) {
1257
1309
  isWeb && isHTMLElement(fieldReference.ref) && isNullOrUndefined(value)
1258
1310
  ? ''
1259
1311
  : value;
1260
- if (isMultipleSelect(fieldReference.ref)) {
1312
+ if (isFileInput(fieldReference.ref) && !isString(fieldValue)) {
1313
+ fieldReference.ref.files = fieldValue;
1314
+ }
1315
+ else if (isMultipleSelect(fieldReference.ref)) {
1261
1316
  [...fieldReference.ref.options].forEach((selectRef) => (selectRef.selected = fieldValue.includes(selectRef.value)));
1262
1317
  }
1263
1318
  else if (fieldReference.refs) {
@@ -1368,18 +1423,11 @@ function createFormControl(props = {}) {
1368
1423
  isValidating: true,
1369
1424
  });
1370
1425
  if (_options.resolver) {
1371
- const { errors } = await executeResolver([name]);
1372
- error = get(errors, name);
1373
- if (isCheckBoxInput(target) && !error) {
1374
- const parentNodeName = getNodeParentName(name);
1375
- const parentField = get(_fields, parentNodeName);
1376
- if (Array.isArray(parentField) &&
1377
- parentField.every((field) => field._f && isCheckBoxInput(field._f.ref))) {
1378
- const parentError = get(errors, parentNodeName, {});
1379
- parentError.type && (error = parentError);
1380
- name = parentNodeName;
1381
- }
1382
- }
1426
+ const { errors } = await _executeSchema([name]);
1427
+ const previousErrorLookupResult = schemaErrorLookup(_formState.errors, _fields, name);
1428
+ const errorLookupResult = schemaErrorLookup(errors, _fields, previousErrorLookupResult.name || name);
1429
+ error = errorLookupResult.error;
1430
+ name = errorLookupResult.name;
1383
1431
  isValid = isEmptyObject(errors);
1384
1432
  }
1385
1433
  else {
@@ -1398,7 +1446,7 @@ function createFormControl(props = {}) {
1398
1446
  isValidating: true,
1399
1447
  });
1400
1448
  if (_options.resolver) {
1401
- const errors = await executeResolverValidation(isUndefined(name) ? name : fieldNames);
1449
+ const errors = await executeSchemaAndUpdateState(isUndefined(name) ? name : fieldNames);
1402
1450
  isValid = isEmptyObject(errors);
1403
1451
  validationResult = name
1404
1452
  ? !fieldNames.some((name) => get(errors, name))
@@ -1414,7 +1462,10 @@ function createFormControl(props = {}) {
1414
1462
  else {
1415
1463
  validationResult = isValid = await executeBuildInValidation(_fields);
1416
1464
  }
1417
- _subjects.state.next(Object.assign(Object.assign({}, (!isString(name) || isValid !== _formState.isValid ? {} : { name })), { errors: _formState.errors, isValid, isValidating: false }));
1465
+ _subjects.state.next(Object.assign(Object.assign(Object.assign({}, (!isString(name) ||
1466
+ (_proxyFormState.isValid && isValid !== _formState.isValid)
1467
+ ? {}
1468
+ : { name })), (_options.resolver ? { isValid } : {})), { errors: _formState.errors, isValidating: false }));
1418
1469
  options.shouldFocus &&
1419
1470
  !validationResult &&
1420
1471
  focusFieldBy(_fields, (key) => get(_formState.errors, key), name ? fieldNames : _names.mount);
@@ -1434,6 +1485,7 @@ function createFormControl(props = {}) {
1434
1485
  : (_formState.errors = {});
1435
1486
  _subjects.state.next({
1436
1487
  errors: _formState.errors,
1488
+ isValid: true,
1437
1489
  });
1438
1490
  };
1439
1491
  const setError = (name, error, options) => {
@@ -1540,7 +1592,7 @@ function createFormControl(props = {}) {
1540
1592
  });
1541
1593
  try {
1542
1594
  if (_options.resolver) {
1543
- const { errors, values } = await executeResolver();
1595
+ const { errors, values } = await _executeSchema();
1544
1596
  _formState.errors = errors;
1545
1597
  fieldValues = values;
1546
1598
  }
@@ -1602,7 +1654,7 @@ function createFormControl(props = {}) {
1602
1654
  }
1603
1655
  _formValues = props.shouldUnregister
1604
1656
  ? keepStateOptions.keepDefaultValues
1605
- ? _defaultValues
1657
+ ? cloneObject(_defaultValues)
1606
1658
  : {}
1607
1659
  : cloneUpdatedValues;
1608
1660
  _fields = {};
@@ -1660,6 +1712,7 @@ function createFormControl(props = {}) {
1660
1712
  control: {
1661
1713
  register,
1662
1714
  unregister,
1715
+ _executeSchema,
1663
1716
  _getWatch,
1664
1717
  _getDirty,
1665
1718
  _updateValid,
@@ -1766,6 +1819,7 @@ function useForm(props = {}) {
1766
1819
  }
1767
1820
  control._removeUnmounted();
1768
1821
  });
1822
+ React.useEffect(() => () => Object.values(control._subjects).forEach((subject) => subject.unsubscribe()), [control]);
1769
1823
  _formControl.current.formState = getProxyFormState(formState, control._proxyFormState);
1770
1824
  return _formControl.current;
1771
1825
  }