react-hook-form 7.11.1 → 7.12.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 +12 -0
- package/README.md +4 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +81 -44
- 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/types/form.d.ts +6 -1
- package/dist/useForm.d.ts +1 -1
- package/dist/utils/debounce.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
@@ -148,11 +148,18 @@ function useController({ name, rules, defaultValue, control, shouldUnregister, }
|
|
148
148
|
control: control || methods.control,
|
149
149
|
name,
|
150
150
|
});
|
151
|
+
function updateIsMounted(name, value) {
|
152
|
+
const field = get(fieldsRef.current, name);
|
153
|
+
if (field && field._f) {
|
154
|
+
field._f.mount = value;
|
155
|
+
}
|
156
|
+
}
|
151
157
|
React.useEffect(() => {
|
152
158
|
const controllerSubscription = subjectsRef.current.control.subscribe({
|
153
159
|
next: (data) => (!data.name || name === data.name) &&
|
154
160
|
setInputStateValue(get(data.values, name)),
|
155
161
|
});
|
162
|
+
updateIsMounted(name, true);
|
156
163
|
return () => {
|
157
164
|
controllerSubscription.unsubscribe();
|
158
165
|
const shouldUnmountField = shouldUnmount || shouldUnregister;
|
@@ -162,10 +169,7 @@ function useController({ name, rules, defaultValue, control, shouldUnregister, }
|
|
162
169
|
unregister(name);
|
163
170
|
}
|
164
171
|
else {
|
165
|
-
|
166
|
-
if (field && field._f) {
|
167
|
-
field._f.mount = false;
|
168
|
-
}
|
172
|
+
updateIsMounted(name, false);
|
169
173
|
}
|
170
174
|
};
|
171
175
|
}, [name]);
|
@@ -370,7 +374,7 @@ function setDirtyFields(values, defaultValues, dirtyFields, parentNode, parentNa
|
|
370
374
|
var setFieldArrayDirtyFields = (values, defaultValues, dirtyFields) => deepMerge(setDirtyFields(values, defaultValues, dirtyFields.slice(0, values.length)), setDirtyFields(defaultValues, values, dirtyFields.slice(0, values.length)));
|
371
375
|
|
372
376
|
function append(data, value) {
|
373
|
-
return [...data, ...convertToArrayPayload(value)];
|
377
|
+
return [...convertToArrayPayload(data), ...convertToArrayPayload(value)];
|
374
378
|
}
|
375
379
|
|
376
380
|
var fillEmptyArray = (value) => Array.isArray(value) ? Array(value.length).fill(undefined) : undefined;
|
@@ -395,7 +399,7 @@ var moveArrayAt = (data, from, to) => {
|
|
395
399
|
};
|
396
400
|
|
397
401
|
function prepend(data, value) {
|
398
|
-
return [...convertToArrayPayload(value), ...data];
|
402
|
+
return [...convertToArrayPayload(value), ...convertToArrayPayload(data)];
|
399
403
|
}
|
400
404
|
|
401
405
|
function removeAtIndexes(data, indexes) {
|
@@ -503,6 +507,8 @@ const useFieldArray = ({ control, name, keyName = 'id', shouldUnregister, }) =>
|
|
503
507
|
cleanup(formStateRef.current.dirtyFields);
|
504
508
|
}
|
505
509
|
subjectsRef.current.state.next({
|
510
|
+
dirtyFields: formStateRef.current
|
511
|
+
.dirtyFields,
|
506
512
|
isDirty: getIsDirty(name, omitKey(updatedFieldArrayValues)),
|
507
513
|
errors: formStateRef.current.errors,
|
508
514
|
isValid: formStateRef.current.isValid,
|
@@ -938,6 +944,14 @@ var validateField = async ({ _f: { ref, refs, required, maxLength, minLength, mi
|
|
938
944
|
return error;
|
939
945
|
};
|
940
946
|
|
947
|
+
var debounce = (callback, wait) => {
|
948
|
+
let timer = 0;
|
949
|
+
return (...args) => {
|
950
|
+
clearTimeout(timer);
|
951
|
+
timer = setTimeout(() => callback(...args), wait);
|
952
|
+
};
|
953
|
+
};
|
954
|
+
|
941
955
|
var getValidationModes = (mode) => ({
|
942
956
|
isOnSubmit: !mode || mode === VALIDATION_MODE.onSubmit,
|
943
957
|
isOnBlur: mode === VALIDATION_MODE.onBlur,
|
@@ -997,7 +1011,7 @@ class Subject {
|
|
997
1011
|
}
|
998
1012
|
|
999
1013
|
const isWindowUndefined = typeof window === 'undefined';
|
1000
|
-
function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_MODE.onChange, resolver, context, defaultValues = {}, shouldFocusError = true, shouldUseNativeValidation, shouldUnregister, criteriaMode, } = {}) {
|
1014
|
+
function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_MODE.onChange, resolver, context, defaultValues = {}, shouldFocusError = true, delayError, shouldUseNativeValidation, shouldUnregister, criteriaMode, } = {}) {
|
1001
1015
|
const [formState, updateFormState] = React.useState({
|
1002
1016
|
isDirty: false,
|
1003
1017
|
isValidating: false,
|
@@ -1026,6 +1040,7 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1026
1040
|
const contextRef = React.useRef(context);
|
1027
1041
|
const inFieldArrayActionRef = React.useRef(false);
|
1028
1042
|
const isMountedRef = React.useRef(false);
|
1043
|
+
const _delayCallback = React.useRef();
|
1029
1044
|
const subjectsRef = React.useRef({
|
1030
1045
|
watch: new Subject(),
|
1031
1046
|
control: new Subject(),
|
@@ -1046,22 +1061,35 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1046
1061
|
const isFieldWatched = (name) => namesRef.current.watchAll ||
|
1047
1062
|
namesRef.current.watch.has(name) ||
|
1048
1063
|
namesRef.current.watch.has((name.match(/\w+/) || [])[0]);
|
1049
|
-
const
|
1064
|
+
const updateErrorState = (name, error) => {
|
1065
|
+
set(formStateRef.current.errors, name, error);
|
1066
|
+
subjectsRef.current.state.next({
|
1067
|
+
errors: formStateRef.current.errors,
|
1068
|
+
});
|
1069
|
+
};
|
1070
|
+
const shouldRenderBaseOnError = React.useCallback(async (shouldSkipRender, name, error, fieldState, isValidFromResolver, isWatched) => {
|
1050
1071
|
const previousError = get(formStateRef.current.errors, name);
|
1051
1072
|
const isValid = readFormStateRef.current.isValid
|
1052
1073
|
? resolver
|
1053
1074
|
? isValidFromResolver
|
1054
1075
|
: await validateForm(fieldsRef.current, true)
|
1055
1076
|
: false;
|
1056
|
-
error
|
1057
|
-
|
1058
|
-
|
1077
|
+
if (delayError && error) {
|
1078
|
+
_delayCallback.current =
|
1079
|
+
_delayCallback.current || debounce(updateErrorState, delayError);
|
1080
|
+
_delayCallback.current(name, error);
|
1081
|
+
}
|
1082
|
+
else {
|
1083
|
+
error
|
1084
|
+
? set(formStateRef.current.errors, name, error)
|
1085
|
+
: unset(formStateRef.current.errors, name);
|
1086
|
+
}
|
1059
1087
|
if ((isWatched ||
|
1060
1088
|
(error ? !deepEqual(previousError, error, true) : previousError) ||
|
1061
|
-
!isEmptyObject(
|
1089
|
+
!isEmptyObject(fieldState) ||
|
1062
1090
|
formStateRef.current.isValid !== isValid) &&
|
1063
1091
|
!shouldSkipRender) {
|
1064
|
-
const updatedFormState = Object.assign(Object.assign({},
|
1092
|
+
const updatedFormState = Object.assign(Object.assign({}, fieldState), { isValid: !!isValid, errors: formStateRef.current.errors, name });
|
1065
1093
|
formStateRef.current = Object.assign(Object.assign({}, formStateRef.current), updatedFormState);
|
1066
1094
|
subjectsRef.current.state.next(isWatched ? { name } : updatedFormState);
|
1067
1095
|
}
|
@@ -1237,7 +1265,7 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1237
1265
|
readFormStateRef.current.isValid && updateIsValid();
|
1238
1266
|
return isValid;
|
1239
1267
|
}, [executeResolverValidation, executeInlineValidation]);
|
1240
|
-
const updateIsValidAndInputValue = (name, ref) => {
|
1268
|
+
const updateIsValidAndInputValue = (name, ref, shouldSkipValueAs) => {
|
1241
1269
|
const field = get(fieldsRef.current, name);
|
1242
1270
|
if (field) {
|
1243
1271
|
const isValueUndefined = isUndefined(field._f.value);
|
@@ -1250,6 +1278,9 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1250
1278
|
if (ref && ref.defaultChecked) {
|
1251
1279
|
field._f.value = getFieldValue(field);
|
1252
1280
|
}
|
1281
|
+
else if (shouldSkipValueAs) {
|
1282
|
+
field._f.value = defaultValue;
|
1283
|
+
}
|
1253
1284
|
else {
|
1254
1285
|
setFieldValue(name, defaultValue);
|
1255
1286
|
}
|
@@ -1264,10 +1295,12 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1264
1295
|
const isValid = resolver
|
1265
1296
|
? isEmptyObject((await resolverRef.current(Object.assign(Object.assign({}, getFieldsValues(fieldsRef)), values), contextRef.current, getResolverOptions(namesRef.current.mount, fieldsRef.current, criteriaMode, shouldUseNativeValidation))).errors)
|
1266
1297
|
: await validateForm(fieldsRef.current, true);
|
1267
|
-
isValid !== formStateRef.current.isValid
|
1298
|
+
if (isValid !== formStateRef.current.isValid) {
|
1299
|
+
formStateRef.current.isValid = isValid;
|
1268
1300
|
subjectsRef.current.state.next({
|
1269
1301
|
isValid,
|
1270
1302
|
});
|
1303
|
+
}
|
1271
1304
|
}, [criteriaMode, shouldUseNativeValidation]);
|
1272
1305
|
const setValues = React.useCallback((name, value, options) => Object.entries(value).forEach(([fieldKey, fieldValue]) => {
|
1273
1306
|
const fieldName = `${name}.${fieldKey}`;
|
@@ -1307,9 +1340,36 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1307
1340
|
isFieldWatched(name) && subjectsRef.current.state.next({});
|
1308
1341
|
subjectsRef.current.watch.next({ name, values: getValues() });
|
1309
1342
|
};
|
1310
|
-
const
|
1343
|
+
const handleValidate = async (target, fieldState, isWatched, isBlurEvent) => {
|
1311
1344
|
let error;
|
1312
1345
|
let isValid;
|
1346
|
+
let name = target.name;
|
1347
|
+
const field = get(fieldsRef.current, name);
|
1348
|
+
if (resolver) {
|
1349
|
+
const { errors } = await resolverRef.current(getFieldsValues(fieldsRef), contextRef.current, getResolverOptions([name], fieldsRef.current, criteriaMode, shouldUseNativeValidation));
|
1350
|
+
error = get(errors, name);
|
1351
|
+
if (isCheckBoxInput(target) && !error) {
|
1352
|
+
const parentNodeName = getNodeParentName(name);
|
1353
|
+
const currentError = get(errors, parentNodeName, {});
|
1354
|
+
currentError.type && currentError.message && (error = currentError);
|
1355
|
+
if (currentError || get(formStateRef.current.errors, parentNodeName)) {
|
1356
|
+
name = parentNodeName;
|
1357
|
+
}
|
1358
|
+
}
|
1359
|
+
isValid = isEmptyObject(errors);
|
1360
|
+
}
|
1361
|
+
else {
|
1362
|
+
error = (await validateField(field, isValidateAllFieldCriteria, shouldUseNativeValidation))[name];
|
1363
|
+
}
|
1364
|
+
!isBlurEvent &&
|
1365
|
+
subjectsRef.current.watch.next({
|
1366
|
+
name,
|
1367
|
+
type: target.type,
|
1368
|
+
values: getValues(),
|
1369
|
+
});
|
1370
|
+
shouldRenderBaseOnError(false, name, error, fieldState, isValid, isWatched);
|
1371
|
+
};
|
1372
|
+
const handleChange = React.useCallback(async ({ type, target, target: { value, name, type: inputType } }) => {
|
1313
1373
|
const field = get(fieldsRef.current, name);
|
1314
1374
|
if (field) {
|
1315
1375
|
let inputValue = inputType ? getFieldValue(field) : undefined;
|
@@ -1325,8 +1385,8 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1325
1385
|
if (!isUndefined(inputValue)) {
|
1326
1386
|
field._f.value = inputValue;
|
1327
1387
|
}
|
1328
|
-
const
|
1329
|
-
const shouldRender = !isEmptyObject(
|
1388
|
+
const fieldState = updateTouchAndDirtyState(name, field._f.value, isBlurEvent, false);
|
1389
|
+
const shouldRender = !isEmptyObject(fieldState) || isWatched;
|
1330
1390
|
if (shouldSkipValidation) {
|
1331
1391
|
!isBlurEvent &&
|
1332
1392
|
subjectsRef.current.watch.next({
|
@@ -1335,35 +1395,12 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1335
1395
|
values: getValues(),
|
1336
1396
|
});
|
1337
1397
|
return (shouldRender &&
|
1338
|
-
subjectsRef.current.state.next(isWatched ? { name } : Object.assign(Object.assign({},
|
1398
|
+
subjectsRef.current.state.next(isWatched ? { name } : Object.assign(Object.assign({}, fieldState), { name })));
|
1339
1399
|
}
|
1340
1400
|
subjectsRef.current.state.next({
|
1341
1401
|
isValidating: true,
|
1342
1402
|
});
|
1343
|
-
|
1344
|
-
const { errors } = await resolverRef.current(getFieldsValues(fieldsRef), contextRef.current, getResolverOptions([name], fieldsRef.current, criteriaMode, shouldUseNativeValidation));
|
1345
|
-
error = get(errors, name);
|
1346
|
-
if (isCheckBoxInput(target) && !error) {
|
1347
|
-
const parentNodeName = getNodeParentName(name);
|
1348
|
-
const currentError = get(errors, parentNodeName, {});
|
1349
|
-
currentError.type && currentError.message && (error = currentError);
|
1350
|
-
if (currentError ||
|
1351
|
-
get(formStateRef.current.errors, parentNodeName)) {
|
1352
|
-
name = parentNodeName;
|
1353
|
-
}
|
1354
|
-
}
|
1355
|
-
isValid = isEmptyObject(errors);
|
1356
|
-
}
|
1357
|
-
else {
|
1358
|
-
error = (await validateField(field, isValidateAllFieldCriteria, shouldUseNativeValidation))[name];
|
1359
|
-
}
|
1360
|
-
!isBlurEvent &&
|
1361
|
-
subjectsRef.current.watch.next({
|
1362
|
-
name,
|
1363
|
-
type,
|
1364
|
-
values: getValues(),
|
1365
|
-
});
|
1366
|
-
shouldRenderBaseOnError(false, name, error, inputState, isValid, isWatched);
|
1403
|
+
handleValidate(target, fieldState, isWatched, isBlurEvent);
|
1367
1404
|
}
|
1368
1405
|
}, []);
|
1369
1406
|
const getValues = (fieldNames) => {
|
@@ -1465,7 +1502,7 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1465
1502
|
_f: Object.assign(Object.assign(Object.assign({}, (field && field._f ? field._f : { ref: { name } })), { name, mount: true }), options),
|
1466
1503
|
});
|
1467
1504
|
namesRef.current.mount.add(name);
|
1468
|
-
!field && updateIsValidAndInputValue(name);
|
1505
|
+
!field && updateIsValidAndInputValue(name, undefined, true);
|
1469
1506
|
return isWindowUndefined
|
1470
1507
|
? { name: name }
|
1471
1508
|
: {
|