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/CHANGELOG.md +1185 -0
- package/README.md +6 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +151 -91
- 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/logic/shouldSubscribeByName.d.ts +2 -0
- package/dist/types/fieldArray.d.ts +1 -1
- package/dist/types/form.d.ts +11 -8
- package/dist/types/utils.d.ts +5 -0
- package/dist/useSubscribe.d.ts +2 -2
- package/dist/useWatch.d.ts +4 -4
- package/dist/utils/cloneObject.d.ts +1 -1
- package/dist/utils/convertToArrayPayload.d.ts +1 -1
- package/dist/utils/createSubject.d.ts +18 -0
- package/dist/utils/get.d.ts +1 -1
- package/package.json +21 -23
- package/dist/utils/Subject.d.ts +0 -24
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) => (
|
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) =>
|
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:
|
1031
|
-
control:
|
1032
|
-
array:
|
1033
|
-
state:
|
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
|
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
|
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
|
1173
|
-
const { errors } = await
|
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
|
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 (
|
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
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
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
|
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) ||
|
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(
|
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
|
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
|
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) =>
|
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: (
|
1767
|
-
if (
|
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)
|