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/CHANGELOG.md +28 -0
- package/README.md +23 -6
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +127 -73
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/logic/schemaErrorLookup.d.ts +5 -0
- package/dist/types/fieldArray.d.ts +1 -1
- package/dist/types/form.d.ts +11 -8
- package/dist/useSubscribe.d.ts +2 -2
- package/dist/utils/createSubject.d.ts +18 -0
- package/package.json +14 -14
- package/dist/utils/subject.d.ts +0 -24
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:
|
1035
|
-
control:
|
1036
|
-
array:
|
1037
|
-
state:
|
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
|
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
|
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
|
1177
|
-
const { errors } = await
|
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 (
|
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
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
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
|
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) ||
|
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
|
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
|
}
|