react-hook-form 7.17.3 → 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,7 @@ 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
105
 
106
106
  var shouldSubscribeByName = (name, signalName) => !name ||
107
107
  !signalName ||
@@ -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) => {
@@ -466,6 +468,15 @@ const useFieldArray = (props) => {
466
468
  }
467
469
  }
468
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
+ });
469
480
  control._subjects.watch.next({
470
481
  name,
471
482
  values: control._formValues,
@@ -523,6 +534,60 @@ function cloneObject(data) {
523
534
  return copy;
524
535
  }
525
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
+
526
591
  var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
527
592
 
528
593
  function deepEqual(object1, object2) {
@@ -566,6 +631,8 @@ var getValidationModes = (mode) => ({
566
631
 
567
632
  var isBoolean = (value) => typeof value === 'boolean';
568
633
 
634
+ var isFileInput = (element) => element.type === 'file';
635
+
569
636
  var isHTMLElement = (value) => value instanceof HTMLElement;
570
637
 
571
638
  var isMultipleSelect = (element) => element.type === `select-multiple`;
@@ -582,52 +649,6 @@ var isWeb = typeof window !== 'undefined' &&
582
649
 
583
650
  var live = (ref) => isHTMLElement(ref) && document.contains(ref);
584
651
 
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
652
  function baseGet(object, updatePath) {
632
653
  const length = updatePath.slice(0, -1).length;
633
654
  let index = 0;
@@ -667,8 +688,6 @@ function unset(object, path) {
667
688
  return object;
668
689
  }
669
690
 
670
- var isFileInput = (element) => element.type === 'file';
671
-
672
691
  const defaultResult = {
673
692
  value: false,
674
693
  isValid: false,
@@ -762,6 +781,35 @@ var hasValidation = (options) => options.mount &&
762
781
  options.pattern ||
763
782
  options.validate);
764
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
+
765
813
  function deepMerge(target, source) {
766
814
  if (isPrimitive(target) || isPrimitive(source)) {
767
815
  return source;
@@ -1031,10 +1079,10 @@ function createFormControl(props = {}) {
1031
1079
  errors: false,
1032
1080
  };
1033
1081
  const _subjects = {
1034
- watch: new Subject(),
1035
- control: new Subject(),
1036
- array: new Subject(),
1037
- state: new Subject(),
1082
+ watch: createSubject(),
1083
+ control: createSubject(),
1084
+ array: createSubject(),
1085
+ state: createSubject(),
1038
1086
  };
1039
1087
  const validationModeBeforeSubmit = getValidationModes(_options.mode);
1040
1088
  const validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
@@ -1051,7 +1099,7 @@ function createFormControl(props = {}) {
1051
1099
  let isValid = false;
1052
1100
  if (_proxyFormState.isValid) {
1053
1101
  isValid = _options.resolver
1054
- ? isEmptyObject((await executeResolver()).errors)
1102
+ ? isEmptyObject((await _executeSchema()).errors)
1055
1103
  : await executeBuildInValidation(_fields, true);
1056
1104
  if (!shouldSkipRender && isValid !== _formState.isValid) {
1057
1105
  _formState.isValid = isValid;
@@ -1170,11 +1218,11 @@ function createFormControl(props = {}) {
1170
1218
  validateFields = {};
1171
1219
  }
1172
1220
  };
1173
- const executeResolver = async (name) => _options.resolver
1221
+ const _executeSchema = async (name) => _options.resolver
1174
1222
  ? await _options.resolver(Object.assign({}, _formValues), _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation))
1175
1223
  : {};
1176
- const executeResolverValidation = async (names) => {
1177
- const { errors } = await executeResolver();
1224
+ const executeSchemaAndUpdateState = async (names) => {
1225
+ const { errors } = await _executeSchema();
1178
1226
  if (names) {
1179
1227
  for (const name of names) {
1180
1228
  const error = get(errors, name);
@@ -1257,7 +1305,10 @@ function createFormControl(props = {}) {
1257
1305
  isWeb && isHTMLElement(fieldReference.ref) && isNullOrUndefined(value)
1258
1306
  ? ''
1259
1307
  : value;
1260
- if (isMultipleSelect(fieldReference.ref)) {
1308
+ if (isFileInput(fieldReference.ref) && !isString(fieldValue)) {
1309
+ fieldReference.ref.files = fieldValue;
1310
+ }
1311
+ else if (isMultipleSelect(fieldReference.ref)) {
1261
1312
  [...fieldReference.ref.options].forEach((selectRef) => (selectRef.selected = fieldValue.includes(selectRef.value)));
1262
1313
  }
1263
1314
  else if (fieldReference.refs) {
@@ -1368,18 +1419,11 @@ function createFormControl(props = {}) {
1368
1419
  isValidating: true,
1369
1420
  });
1370
1421
  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
- }
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;
1383
1427
  isValid = isEmptyObject(errors);
1384
1428
  }
1385
1429
  else {
@@ -1398,7 +1442,7 @@ function createFormControl(props = {}) {
1398
1442
  isValidating: true,
1399
1443
  });
1400
1444
  if (_options.resolver) {
1401
- const errors = await executeResolverValidation(isUndefined(name) ? name : fieldNames);
1445
+ const errors = await executeSchemaAndUpdateState(isUndefined(name) ? name : fieldNames);
1402
1446
  isValid = isEmptyObject(errors);
1403
1447
  validationResult = name
1404
1448
  ? !fieldNames.some((name) => get(errors, name))
@@ -1414,7 +1458,10 @@ function createFormControl(props = {}) {
1414
1458
  else {
1415
1459
  validationResult = isValid = await executeBuildInValidation(_fields);
1416
1460
  }
1417
- _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 }));
1418
1465
  options.shouldFocus &&
1419
1466
  !validationResult &&
1420
1467
  focusFieldBy(_fields, (key) => get(_formState.errors, key), name ? fieldNames : _names.mount);
@@ -1434,6 +1481,7 @@ function createFormControl(props = {}) {
1434
1481
  : (_formState.errors = {});
1435
1482
  _subjects.state.next({
1436
1483
  errors: _formState.errors,
1484
+ isValid: true,
1437
1485
  });
1438
1486
  };
1439
1487
  const setError = (name, error, options) => {
@@ -1540,7 +1588,7 @@ function createFormControl(props = {}) {
1540
1588
  });
1541
1589
  try {
1542
1590
  if (_options.resolver) {
1543
- const { errors, values } = await executeResolver();
1591
+ const { errors, values } = await _executeSchema();
1544
1592
  _formState.errors = errors;
1545
1593
  fieldValues = values;
1546
1594
  }
@@ -1600,7 +1648,11 @@ function createFormControl(props = {}) {
1600
1648
  }
1601
1649
  }
1602
1650
  }
1603
- _formValues = props.shouldUnregister ? {} : cloneUpdatedValues;
1651
+ _formValues = props.shouldUnregister
1652
+ ? keepStateOptions.keepDefaultValues
1653
+ ? cloneObject(_defaultValues)
1654
+ : {}
1655
+ : cloneUpdatedValues;
1604
1656
  _fields = {};
1605
1657
  _subjects.control.next({
1606
1658
  values: hasUpdatedFormValues ? cloneUpdatedValues : _defaultValues,
@@ -1649,13 +1701,14 @@ function createFormControl(props = {}) {
1649
1701
  _stateFlags.watch = !!props.shouldUnregister;
1650
1702
  };
1651
1703
  const setFocus = (name) => {
1652
- const ref = get(_fields, name)._f;
1653
- (ref.focus || ref.refs[0]).focus();
1704
+ const field = get(_fields, name)._f;
1705
+ (field.ref.focus ? field.ref : field.refs[0]).focus();
1654
1706
  };
1655
1707
  return {
1656
1708
  control: {
1657
1709
  register,
1658
1710
  unregister,
1711
+ _executeSchema,
1659
1712
  _getWatch,
1660
1713
  _getDirty,
1661
1714
  _updateValid,
@@ -1762,6 +1815,7 @@ function useForm(props = {}) {
1762
1815
  }
1763
1816
  control._removeUnmounted();
1764
1817
  });
1818
+ React.useEffect(() => () => Object.values(control._subjects).forEach((subject) => subject.unsubscribe()), [control]);
1765
1819
  _formControl.current.formState = getProxyFormState(formState, control._proxyFormState);
1766
1820
  return _formControl.current;
1767
1821
  }