react-hook-form 7.11.2-beta.0 → 7.12.2
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 +185 -136
- 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/getFieldsValues.d.ts +4 -0
- package/dist/logic/getResolverOptions.d.ts +1 -1
- package/dist/logic/hasValidation.d.ts +0 -1
- package/dist/logic/validateField.d.ts +1 -1
- package/dist/types/controller.d.ts +1 -1
- package/dist/types/fields.d.ts +1 -0
- package/dist/types/form.d.ts +6 -2
- package/dist/types/validator.d.ts +0 -1
- package/dist/useForm.d.ts +1 -1
- package/dist/utils/debounce.d.ts +2 -0
- package/dist/utils/deepEqual.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
@@ -136,10 +136,10 @@ function useFormState(props) {
|
|
136
136
|
|
137
137
|
function useController({ name, rules, defaultValue, control, shouldUnregister, }) {
|
138
138
|
const methods = useFormContext();
|
139
|
-
const { defaultValuesRef, register, fieldsRef, unregister, namesRef, subjectsRef, shouldUnmount, inFieldArrayActionRef,
|
140
|
-
const
|
141
|
-
const [value, setInputStateValue] = React.useState(!isUndefined(
|
142
|
-
?
|
139
|
+
const { defaultValuesRef, register, fieldsRef, unregister, namesRef, subjectsRef, shouldUnmount, inFieldArrayActionRef, } = control || methods.control;
|
140
|
+
const field = get(fieldsRef.current, name);
|
141
|
+
const [value, setInputStateValue] = React.useState(field && field._f && !isUndefined(field._f.value)
|
142
|
+
? field._f.value
|
143
143
|
: isUndefined(get(defaultValuesRef.current, name))
|
144
144
|
? defaultValue
|
145
145
|
: get(defaultValuesRef.current, name));
|
@@ -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]);
|
@@ -263,6 +267,28 @@ const focusFieldBy = (fields, callback, fieldsNames) => {
|
|
263
267
|
}
|
264
268
|
};
|
265
269
|
|
270
|
+
const getFieldsValues = (fieldsRef, output = {}) => {
|
271
|
+
for (const name in fieldsRef.current) {
|
272
|
+
const field = fieldsRef.current[name];
|
273
|
+
if (field && !isNullOrUndefined(output)) {
|
274
|
+
const _f = field._f;
|
275
|
+
const current = omit(field, '_f');
|
276
|
+
set(output, name, _f && _f.ref
|
277
|
+
? _f.ref.disabled || (_f.refs && _f.refs.every((ref) => ref.disabled))
|
278
|
+
? undefined
|
279
|
+
: _f.value
|
280
|
+
: Array.isArray(field)
|
281
|
+
? []
|
282
|
+
: {});
|
283
|
+
current &&
|
284
|
+
getFieldsValues({
|
285
|
+
current,
|
286
|
+
}, output[name]);
|
287
|
+
}
|
288
|
+
}
|
289
|
+
return output;
|
290
|
+
};
|
291
|
+
|
266
292
|
var generateId = () => {
|
267
293
|
const d = typeof performance === 'undefined' ? Date.now() : performance.now() * 1000;
|
268
294
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
@@ -275,29 +301,30 @@ var mapIds = (values = [], keyName) => values.map((value) => (Object.assign({ [k
|
|
275
301
|
|
276
302
|
var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
|
277
303
|
|
278
|
-
function deepEqual(object1, object2
|
304
|
+
function deepEqual(object1, object2) {
|
279
305
|
if (isPrimitive(object1) ||
|
280
306
|
isPrimitive(object2) ||
|
281
307
|
isDateObject(object1) ||
|
282
308
|
isDateObject(object2)) {
|
283
309
|
return object1 === object2;
|
284
310
|
}
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
311
|
+
const keys1 = Object.keys(object1);
|
312
|
+
const keys2 = Object.keys(object2);
|
313
|
+
if (keys1.length !== keys2.length) {
|
314
|
+
return false;
|
315
|
+
}
|
316
|
+
for (const key of keys1) {
|
317
|
+
const val1 = object1[key];
|
318
|
+
if (!keys2.includes(key)) {
|
289
319
|
return false;
|
290
320
|
}
|
291
|
-
|
292
|
-
const
|
293
|
-
if (
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
: val1 !== val2) {
|
299
|
-
return false;
|
300
|
-
}
|
321
|
+
if (key !== 'ref') {
|
322
|
+
const val2 = object2[key];
|
323
|
+
if ((isObject(val1) || Array.isArray(val1)) &&
|
324
|
+
(isObject(val2) || Array.isArray(val2))
|
325
|
+
? !deepEqual(val1, val2)
|
326
|
+
: val1 !== val2) {
|
327
|
+
return false;
|
301
328
|
}
|
302
329
|
}
|
303
330
|
}
|
@@ -348,7 +375,7 @@ function setDirtyFields(values, defaultValues, dirtyFields, parentNode, parentNa
|
|
348
375
|
var setFieldArrayDirtyFields = (values, defaultValues, dirtyFields) => deepMerge(setDirtyFields(values, defaultValues, dirtyFields.slice(0, values.length)), setDirtyFields(defaultValues, values, dirtyFields.slice(0, values.length)));
|
349
376
|
|
350
377
|
function append(data, value) {
|
351
|
-
return [...data, ...convertToArrayPayload(value)];
|
378
|
+
return [...convertToArrayPayload(data), ...convertToArrayPayload(value)];
|
352
379
|
}
|
353
380
|
|
354
381
|
var fillEmptyArray = (value) => Array.isArray(value) ? Array(value.length).fill(undefined) : undefined;
|
@@ -373,7 +400,7 @@ var moveArrayAt = (data, from, to) => {
|
|
373
400
|
};
|
374
401
|
|
375
402
|
function prepend(data, value) {
|
376
|
-
return [...convertToArrayPayload(value), ...data];
|
403
|
+
return [...convertToArrayPayload(value), ...convertToArrayPayload(data)];
|
377
404
|
}
|
378
405
|
|
379
406
|
function removeAtIndexes(data, indexes) {
|
@@ -438,9 +465,9 @@ const useFieldArray = ({ control, name, keyName = 'id', shouldUnregister, }) =>
|
|
438
465
|
const methods = useFormContext();
|
439
466
|
const focusNameRef = React.useRef('');
|
440
467
|
const isMountedRef = React.useRef(false);
|
441
|
-
const { getIsDirty, namesRef, fieldsRef, defaultValuesRef, formStateRef, subjectsRef, readFormStateRef, updateIsValid, fieldArrayDefaultValuesRef, unregister, shouldUnmount, inFieldArrayActionRef, setValues, register,
|
442
|
-
const [fields, setFields] = React.useState(mapIds((get(
|
443
|
-
? get(
|
468
|
+
const { getIsDirty, namesRef, fieldsRef, defaultValuesRef, formStateRef, subjectsRef, readFormStateRef, updateIsValid, fieldArrayDefaultValuesRef, unregister, shouldUnmount, inFieldArrayActionRef, setValues, register, } = control || methods.control;
|
469
|
+
const [fields, setFields] = React.useState(mapIds((get(fieldsRef.current, name) && isMountedRef.current
|
470
|
+
? get(getFieldsValues(fieldsRef), name)
|
444
471
|
: get(fieldArrayDefaultValuesRef.current, getNodeParentName(name))
|
445
472
|
? get(fieldArrayDefaultValuesRef.current, name)
|
446
473
|
: get(defaultValuesRef.current, name)) || [], keyName));
|
@@ -448,7 +475,7 @@ const useFieldArray = ({ control, name, keyName = 'id', shouldUnregister, }) =>
|
|
448
475
|
namesRef.current.array.add(name);
|
449
476
|
const omitKey = (fields) => fields.map((field = {}) => omit(field, keyName));
|
450
477
|
const getCurrentFieldsValues = () => {
|
451
|
-
const values = get(
|
478
|
+
const values = get(getFieldsValues(fieldsRef), name, []);
|
452
479
|
return mapIds(get(fieldArrayDefaultValuesRef.current, name, []).map((item, index) => (Object.assign(Object.assign({}, item), values[index]))), keyName);
|
453
480
|
};
|
454
481
|
const getFocusFieldName = (index, options) => options && !options.shouldFocus
|
@@ -462,10 +489,6 @@ const useFieldArray = ({ control, name, keyName = 'id', shouldUnregister, }) =>
|
|
462
489
|
const output = method(get(fieldsRef.current, name), args.argA, args.argB);
|
463
490
|
shouldSet && set(fieldsRef.current, name, output);
|
464
491
|
}
|
465
|
-
if (get(_formValues.current, name)) {
|
466
|
-
const output = method(get(_formValues.current, name), args.argA, args.argB);
|
467
|
-
shouldSet && set(_formValues.current, name, output);
|
468
|
-
}
|
469
492
|
if (Array.isArray(get(formStateRef.current.errors, name))) {
|
470
493
|
const output = method(get(formStateRef.current.errors, name), args.argA, args.argB);
|
471
494
|
shouldSet && set(formStateRef.current.errors, name, output);
|
@@ -485,6 +508,8 @@ const useFieldArray = ({ control, name, keyName = 'id', shouldUnregister, }) =>
|
|
485
508
|
cleanup(formStateRef.current.dirtyFields);
|
486
509
|
}
|
487
510
|
subjectsRef.current.state.next({
|
511
|
+
dirtyFields: formStateRef.current
|
512
|
+
.dirtyFields,
|
488
513
|
isDirty: getIsDirty(name, omitKey(updatedFieldArrayValues)),
|
489
514
|
errors: formStateRef.current.errors,
|
490
515
|
isValid: formStateRef.current.isValid,
|
@@ -585,7 +610,7 @@ const useFieldArray = ({ control, name, keyName = 'id', shouldUnregister, }) =>
|
|
585
610
|
}
|
586
611
|
subjectsRef.current.watch.next({
|
587
612
|
name,
|
588
|
-
values:
|
613
|
+
values: getFieldsValues(fieldsRef),
|
589
614
|
});
|
590
615
|
focusNameRef.current &&
|
591
616
|
focusFieldBy(fieldsRef.current, (key) => key.startsWith(focusNameRef.current));
|
@@ -601,7 +626,6 @@ const useFieldArray = ({ control, name, keyName = 'id', shouldUnregister, }) =>
|
|
601
626
|
next({ name: inputFieldArrayName, values, isReset }) {
|
602
627
|
if (isReset) {
|
603
628
|
unset(fieldsRef.current, inputFieldArrayName || name);
|
604
|
-
unset(_formValues.current, inputFieldArrayName || name);
|
605
629
|
inputFieldArrayName
|
606
630
|
? set(fieldArrayDefaultValuesRef.current, inputFieldArrayName, values)
|
607
631
|
: (fieldArrayDefaultValuesRef.current = values);
|
@@ -609,7 +633,7 @@ const useFieldArray = ({ control, name, keyName = 'id', shouldUnregister, }) =>
|
|
609
633
|
}
|
610
634
|
},
|
611
635
|
});
|
612
|
-
!get(
|
636
|
+
!get(fieldsRef.current, name) && set(fieldsRef.current, name, []);
|
613
637
|
isMountedRef.current = true;
|
614
638
|
return () => {
|
615
639
|
fieldArraySubscription.unsubscribe();
|
@@ -618,7 +642,7 @@ const useFieldArray = ({ control, name, keyName = 'id', shouldUnregister, }) =>
|
|
618
642
|
unset(fieldArrayDefaultValuesRef.current, name);
|
619
643
|
}
|
620
644
|
else {
|
621
|
-
const fieldArrayValues = get(
|
645
|
+
const fieldArrayValues = get(getFieldsValues(fieldsRef), name);
|
622
646
|
fieldArrayValues &&
|
623
647
|
set(fieldArrayDefaultValuesRef.current, name, fieldArrayValues);
|
624
648
|
}
|
@@ -699,7 +723,7 @@ var getRadioValue = (options) => Array.isArray(options)
|
|
699
723
|
function getFieldValue(field) {
|
700
724
|
if (field && field._f) {
|
701
725
|
const ref = field._f.ref;
|
702
|
-
if (
|
726
|
+
if (ref.disabled) {
|
703
727
|
return;
|
704
728
|
}
|
705
729
|
if (isFileInput(ref)) {
|
@@ -785,8 +809,7 @@ var getValueAndMessage = (validationData) => isObject(validationData) && !isRege
|
|
785
809
|
message: '',
|
786
810
|
};
|
787
811
|
|
788
|
-
var validateField = async (
|
789
|
-
const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, } = field._f;
|
812
|
+
var validateField = async ({ _f: { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, value: inputValue, valueAsNumber, mount, }, }, validateAllFieldCriteria, shouldUseNativeValidation) => {
|
790
813
|
if (!mount) {
|
791
814
|
return {};
|
792
815
|
}
|
@@ -922,6 +945,14 @@ var validateField = async (field, inputValue, validateAllFieldCriteria, shouldUs
|
|
922
945
|
return error;
|
923
946
|
};
|
924
947
|
|
948
|
+
var debounce = (callback, wait) => {
|
949
|
+
let timer = 0;
|
950
|
+
return (...args) => {
|
951
|
+
clearTimeout(timer);
|
952
|
+
timer = setTimeout(() => callback(...args), wait);
|
953
|
+
};
|
954
|
+
};
|
955
|
+
|
925
956
|
var getValidationModes = (mode) => ({
|
926
957
|
isOnSubmit: !mode || mode === VALIDATION_MODE.onSubmit,
|
927
958
|
isOnBlur: mode === VALIDATION_MODE.onBlur,
|
@@ -981,7 +1012,7 @@ class Subject {
|
|
981
1012
|
}
|
982
1013
|
|
983
1014
|
const isWindowUndefined = typeof window === 'undefined';
|
984
|
-
function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_MODE.onChange, resolver, context, defaultValues = {}, shouldFocusError = true, shouldUseNativeValidation, shouldUnregister, criteriaMode, } = {}) {
|
1015
|
+
function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_MODE.onChange, resolver, context, defaultValues = {}, shouldFocusError = true, delayError, shouldUseNativeValidation, shouldUnregister, criteriaMode, } = {}) {
|
985
1016
|
const [formState, updateFormState] = React.useState({
|
986
1017
|
isDirty: false,
|
987
1018
|
isValidating: false,
|
@@ -1005,12 +1036,12 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1005
1036
|
const resolverRef = React.useRef(resolver);
|
1006
1037
|
const formStateRef = React.useRef(formState);
|
1007
1038
|
const fieldsRef = React.useRef({});
|
1008
|
-
const _formValues = React.useRef({});
|
1009
1039
|
const defaultValuesRef = React.useRef(defaultValues);
|
1010
1040
|
const fieldArrayDefaultValuesRef = React.useRef({});
|
1011
1041
|
const contextRef = React.useRef(context);
|
1012
1042
|
const inFieldArrayActionRef = React.useRef(false);
|
1013
1043
|
const isMountedRef = React.useRef(false);
|
1044
|
+
const _delayCallback = React.useRef();
|
1014
1045
|
const subjectsRef = React.useRef({
|
1015
1046
|
watch: new Subject(),
|
1016
1047
|
control: new Subject(),
|
@@ -1031,22 +1062,44 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1031
1062
|
const isFieldWatched = (name) => namesRef.current.watchAll ||
|
1032
1063
|
namesRef.current.watch.has(name) ||
|
1033
1064
|
namesRef.current.watch.has((name.match(/\w+/) || [])[0]);
|
1034
|
-
const
|
1065
|
+
const updateErrorState = (name, error) => {
|
1066
|
+
set(formStateRef.current.errors, name, error);
|
1067
|
+
subjectsRef.current.state.next({
|
1068
|
+
errors: formStateRef.current.errors,
|
1069
|
+
});
|
1070
|
+
};
|
1071
|
+
const shouldRenderBaseOnValid = async () => {
|
1072
|
+
const isValid = await validateForm(fieldsRef.current, true);
|
1073
|
+
if (isValid !== formStateRef.current.isValid) {
|
1074
|
+
formStateRef.current.isValid = isValid;
|
1075
|
+
subjectsRef.current.state.next({
|
1076
|
+
isValid,
|
1077
|
+
});
|
1078
|
+
}
|
1079
|
+
};
|
1080
|
+
const shouldRenderBaseOnError = React.useCallback(async (shouldSkipRender, name, error, fieldState, isValidFromResolver, isWatched) => {
|
1035
1081
|
const previousError = get(formStateRef.current.errors, name);
|
1036
1082
|
const isValid = readFormStateRef.current.isValid
|
1037
1083
|
? resolver
|
1038
1084
|
? isValidFromResolver
|
1039
|
-
:
|
1085
|
+
: shouldRenderBaseOnValid()
|
1040
1086
|
: false;
|
1041
|
-
error
|
1042
|
-
|
1043
|
-
|
1087
|
+
if (delayError && error) {
|
1088
|
+
_delayCallback.current =
|
1089
|
+
_delayCallback.current || debounce(updateErrorState, delayError);
|
1090
|
+
_delayCallback.current(name, error);
|
1091
|
+
}
|
1092
|
+
else {
|
1093
|
+
error
|
1094
|
+
? set(formStateRef.current.errors, name, error)
|
1095
|
+
: unset(formStateRef.current.errors, name);
|
1096
|
+
}
|
1044
1097
|
if ((isWatched ||
|
1045
|
-
(error ? !deepEqual(previousError, error
|
1046
|
-
!isEmptyObject(
|
1098
|
+
(error ? !deepEqual(previousError, error) : previousError) ||
|
1099
|
+
!isEmptyObject(fieldState) ||
|
1047
1100
|
formStateRef.current.isValid !== isValid) &&
|
1048
1101
|
!shouldSkipRender) {
|
1049
|
-
const updatedFormState = Object.assign(Object.assign({},
|
1102
|
+
const updatedFormState = Object.assign(Object.assign(Object.assign({}, fieldState), (resolver ? { isValid: !!isValid } : {})), { errors: formStateRef.current.errors, name });
|
1050
1103
|
formStateRef.current = Object.assign(Object.assign({}, formStateRef.current), updatedFormState);
|
1051
1104
|
subjectsRef.current.state.next(isWatched ? { name } : updatedFormState);
|
1052
1105
|
}
|
@@ -1063,8 +1116,7 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1063
1116
|
const value = isWeb && isHTMLElement(_f.ref) && isNullOrUndefined(rawValue)
|
1064
1117
|
? ''
|
1065
1118
|
: rawValue;
|
1066
|
-
|
1067
|
-
_f.value = fieldValue;
|
1119
|
+
_f.value = getFieldValueAs(rawValue, _f);
|
1068
1120
|
if (isRadioInput(_f.ref)) {
|
1069
1121
|
(_f.refs || []).forEach((radioRef) => (radioRef.checked = radioRef.value === value));
|
1070
1122
|
}
|
@@ -1084,10 +1136,11 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1084
1136
|
else {
|
1085
1137
|
_f.ref.value = value;
|
1086
1138
|
}
|
1087
|
-
set(_formValues.current, name, fieldValue);
|
1088
1139
|
if (shouldRender) {
|
1140
|
+
const values = getFieldsValues(fieldsRef);
|
1141
|
+
set(values, name, rawValue);
|
1089
1142
|
subjectsRef.current.control.next({
|
1090
|
-
values: Object.assign(Object.assign({}, defaultValuesRef.current),
|
1143
|
+
values: Object.assign(Object.assign({}, defaultValuesRef.current), values),
|
1091
1144
|
name,
|
1092
1145
|
});
|
1093
1146
|
}
|
@@ -1103,13 +1156,13 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1103
1156
|
},
|
1104
1157
|
value: rawValue,
|
1105
1158
|
};
|
1106
|
-
set(_formValues.current, name, rawValue);
|
1107
1159
|
}
|
1108
1160
|
}
|
1109
1161
|
}, []);
|
1110
1162
|
const getIsDirty = React.useCallback((name, data) => {
|
1111
|
-
|
1112
|
-
|
1163
|
+
const formValues = getFieldsValues(fieldsRef);
|
1164
|
+
name && data && set(formValues, name, data);
|
1165
|
+
return !deepEqual(formValues, defaultValuesRef.current);
|
1113
1166
|
}, []);
|
1114
1167
|
const updateTouchAndDirtyState = React.useCallback((name, inputValue, isCurrentTouched, shouldRender = true) => {
|
1115
1168
|
const state = {
|
@@ -1146,12 +1199,12 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1146
1199
|
return isChanged ? state : {};
|
1147
1200
|
}, []);
|
1148
1201
|
const executeInlineValidation = React.useCallback(async (name, skipReRender) => {
|
1149
|
-
const error = (await validateField(get(fieldsRef.current, name),
|
1202
|
+
const error = (await validateField(get(fieldsRef.current, name), isValidateAllFieldCriteria, shouldUseNativeValidation))[name];
|
1150
1203
|
await shouldRenderBaseOnError(skipReRender, name, error);
|
1151
1204
|
return isUndefined(error);
|
1152
1205
|
}, [isValidateAllFieldCriteria]);
|
1153
1206
|
const executeResolverValidation = React.useCallback(async (names) => {
|
1154
|
-
const { errors } = await resolverRef.current(
|
1207
|
+
const { errors } = await resolverRef.current(getFieldsValues(fieldsRef), contextRef.current, getResolverOptions(namesRef.current.mount, fieldsRef.current, criteriaMode, shouldUseNativeValidation));
|
1155
1208
|
if (names) {
|
1156
1209
|
for (const name of names) {
|
1157
1210
|
const error = get(errors, name);
|
@@ -1174,7 +1227,7 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1174
1227
|
const _f = field._f;
|
1175
1228
|
const current = omit(field, '_f');
|
1176
1229
|
if (_f) {
|
1177
|
-
const fieldError = await validateField(field,
|
1230
|
+
const fieldError = await validateField(field, isValidateAllFieldCriteria, shouldUseNativeValidation);
|
1178
1231
|
if (shouldCheckValid) {
|
1179
1232
|
if (fieldError[_f.name]) {
|
1180
1233
|
context.valid = false;
|
@@ -1222,38 +1275,42 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1222
1275
|
readFormStateRef.current.isValid && updateIsValid();
|
1223
1276
|
return isValid;
|
1224
1277
|
}, [executeResolverValidation, executeInlineValidation]);
|
1225
|
-
const updateIsValidAndInputValue = (name, ref) => {
|
1278
|
+
const updateIsValidAndInputValue = (name, ref, shouldSkipValueAs) => {
|
1226
1279
|
const field = get(fieldsRef.current, name);
|
1227
|
-
const fieldValue = get(getValues(), name);
|
1228
1280
|
if (field) {
|
1229
|
-
const isValueUndefined = isUndefined(
|
1281
|
+
const isValueUndefined = isUndefined(field._f.value);
|
1230
1282
|
const defaultValue = isValueUndefined
|
1231
1283
|
? isUndefined(get(fieldArrayDefaultValuesRef.current, name))
|
1232
1284
|
? get(defaultValuesRef.current, name)
|
1233
1285
|
: get(fieldArrayDefaultValuesRef.current, name)
|
1234
|
-
:
|
1286
|
+
: field._f.value;
|
1235
1287
|
if (!isUndefined(defaultValue)) {
|
1236
1288
|
if (ref && ref.defaultChecked) {
|
1237
|
-
|
1289
|
+
field._f.value = getFieldValue(field);
|
1290
|
+
}
|
1291
|
+
else if (shouldSkipValueAs) {
|
1292
|
+
field._f.value = defaultValue;
|
1238
1293
|
}
|
1239
1294
|
else {
|
1240
1295
|
setFieldValue(name, defaultValue);
|
1241
1296
|
}
|
1242
1297
|
}
|
1243
1298
|
else if (isValueUndefined) {
|
1244
|
-
|
1299
|
+
field._f.value = getFieldValue(field);
|
1245
1300
|
}
|
1246
1301
|
}
|
1247
1302
|
isMountedRef.current && readFormStateRef.current.isValid && updateIsValid();
|
1248
1303
|
};
|
1249
|
-
const updateIsValid = React.useCallback(async () => {
|
1304
|
+
const updateIsValid = React.useCallback(async (values = {}) => {
|
1250
1305
|
const isValid = resolver
|
1251
|
-
? isEmptyObject((await resolverRef.current(
|
1306
|
+
? isEmptyObject((await resolverRef.current(Object.assign(Object.assign({}, getFieldsValues(fieldsRef)), values), contextRef.current, getResolverOptions(namesRef.current.mount, fieldsRef.current, criteriaMode, shouldUseNativeValidation))).errors)
|
1252
1307
|
: await validateForm(fieldsRef.current, true);
|
1253
|
-
isValid !== formStateRef.current.isValid
|
1308
|
+
if (isValid !== formStateRef.current.isValid) {
|
1309
|
+
formStateRef.current.isValid = isValid;
|
1254
1310
|
subjectsRef.current.state.next({
|
1255
1311
|
isValid,
|
1256
1312
|
});
|
1313
|
+
}
|
1257
1314
|
}, [criteriaMode, shouldUseNativeValidation]);
|
1258
1315
|
const setValues = React.useCallback((name, value, options) => Object.entries(value).forEach(([fieldKey, fieldValue]) => {
|
1259
1316
|
const fieldName = `${name}.${fieldKey}`;
|
@@ -1284,21 +1341,45 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1284
1341
|
});
|
1285
1342
|
}
|
1286
1343
|
!value.length &&
|
1344
|
+
set(fieldsRef.current, name, []) &&
|
1287
1345
|
set(fieldArrayDefaultValuesRef.current, name, []);
|
1288
1346
|
}
|
1289
|
-
set(_formValues.current, name, value);
|
1290
1347
|
((field && !field._f) || isFieldArray) && !isNullOrUndefined(value)
|
1291
1348
|
? setValues(name, value, isFieldArray ? {} : options)
|
1292
1349
|
: setFieldValue(name, value, options, true, !field);
|
1293
1350
|
isFieldWatched(name) && subjectsRef.current.state.next({});
|
1294
|
-
subjectsRef.current.watch.next({
|
1295
|
-
name,
|
1296
|
-
values: getValues(),
|
1297
|
-
});
|
1351
|
+
subjectsRef.current.watch.next({ name, values: getValues() });
|
1298
1352
|
};
|
1299
|
-
const
|
1353
|
+
const handleValidate = async (target, fieldState, isWatched, isBlurEvent) => {
|
1300
1354
|
let error;
|
1301
1355
|
let isValid;
|
1356
|
+
let name = target.name;
|
1357
|
+
const field = get(fieldsRef.current, name);
|
1358
|
+
if (resolver) {
|
1359
|
+
const { errors } = await resolverRef.current(getFieldsValues(fieldsRef), contextRef.current, getResolverOptions([name], fieldsRef.current, criteriaMode, shouldUseNativeValidation));
|
1360
|
+
error = get(errors, name);
|
1361
|
+
if (isCheckBoxInput(target) && !error) {
|
1362
|
+
const parentNodeName = getNodeParentName(name);
|
1363
|
+
const currentError = get(errors, parentNodeName, {});
|
1364
|
+
currentError.type && currentError.message && (error = currentError);
|
1365
|
+
if (currentError || get(formStateRef.current.errors, parentNodeName)) {
|
1366
|
+
name = parentNodeName;
|
1367
|
+
}
|
1368
|
+
}
|
1369
|
+
isValid = isEmptyObject(errors);
|
1370
|
+
}
|
1371
|
+
else {
|
1372
|
+
error = (await validateField(field, isValidateAllFieldCriteria, shouldUseNativeValidation))[name];
|
1373
|
+
}
|
1374
|
+
!isBlurEvent &&
|
1375
|
+
subjectsRef.current.watch.next({
|
1376
|
+
name,
|
1377
|
+
type: target.type,
|
1378
|
+
values: getValues(),
|
1379
|
+
});
|
1380
|
+
shouldRenderBaseOnError(false, name, error, fieldState, isValid, isWatched);
|
1381
|
+
};
|
1382
|
+
const handleChange = React.useCallback(async ({ type, target, target: { value, name, type: inputType } }) => {
|
1302
1383
|
const field = get(fieldsRef.current, name);
|
1303
1384
|
if (field) {
|
1304
1385
|
let inputValue = inputType ? getFieldValue(field) : undefined;
|
@@ -1312,10 +1393,10 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1312
1393
|
isReValidateOnChange }, validationMode));
|
1313
1394
|
const isWatched = !isBlurEvent && isFieldWatched(name);
|
1314
1395
|
if (!isUndefined(inputValue)) {
|
1315
|
-
|
1396
|
+
field._f.value = inputValue;
|
1316
1397
|
}
|
1317
|
-
const
|
1318
|
-
const shouldRender = !isEmptyObject(
|
1398
|
+
const fieldState = updateTouchAndDirtyState(name, field._f.value, isBlurEvent, false);
|
1399
|
+
const shouldRender = !isEmptyObject(fieldState) || isWatched;
|
1319
1400
|
if (shouldSkipValidation) {
|
1320
1401
|
!isBlurEvent &&
|
1321
1402
|
subjectsRef.current.watch.next({
|
@@ -1324,39 +1405,16 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1324
1405
|
values: getValues(),
|
1325
1406
|
});
|
1326
1407
|
return (shouldRender &&
|
1327
|
-
subjectsRef.current.state.next(isWatched ? { name } : Object.assign(Object.assign({},
|
1408
|
+
subjectsRef.current.state.next(isWatched ? { name } : Object.assign(Object.assign({}, fieldState), { name })));
|
1328
1409
|
}
|
1329
1410
|
subjectsRef.current.state.next({
|
1330
1411
|
isValidating: true,
|
1331
1412
|
});
|
1332
|
-
|
1333
|
-
const { errors } = await resolverRef.current(getValues(), contextRef.current, getResolverOptions([name], fieldsRef.current, criteriaMode, shouldUseNativeValidation));
|
1334
|
-
error = get(errors, name);
|
1335
|
-
if (isCheckBoxInput(target) && !error) {
|
1336
|
-
const parentNodeName = getNodeParentName(name);
|
1337
|
-
const currentError = get(errors, parentNodeName, {});
|
1338
|
-
currentError.type && currentError.message && (error = currentError);
|
1339
|
-
if (currentError ||
|
1340
|
-
get(formStateRef.current.errors, parentNodeName)) {
|
1341
|
-
name = parentNodeName;
|
1342
|
-
}
|
1343
|
-
}
|
1344
|
-
isValid = isEmptyObject(errors);
|
1345
|
-
}
|
1346
|
-
else {
|
1347
|
-
error = (await validateField(field, get(getValues(), name), isValidateAllFieldCriteria, shouldUseNativeValidation))[name];
|
1348
|
-
}
|
1349
|
-
!isBlurEvent &&
|
1350
|
-
subjectsRef.current.watch.next({
|
1351
|
-
name,
|
1352
|
-
type,
|
1353
|
-
values: getValues(),
|
1354
|
-
});
|
1355
|
-
shouldRenderBaseOnError(false, name, error, inputState, isValid, isWatched);
|
1413
|
+
handleValidate(target, fieldState, isWatched, isBlurEvent);
|
1356
1414
|
}
|
1357
1415
|
}, []);
|
1358
1416
|
const getValues = (fieldNames) => {
|
1359
|
-
const values = Object.assign(Object.assign({}, defaultValuesRef.current),
|
1417
|
+
const values = Object.assign(Object.assign({}, defaultValuesRef.current), getFieldsValues(fieldsRef));
|
1360
1418
|
return isUndefined(fieldNames)
|
1361
1419
|
? values
|
1362
1420
|
: isString(fieldNames)
|
@@ -1383,12 +1441,12 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1383
1441
|
};
|
1384
1442
|
const watchInternal = React.useCallback((fieldNames, defaultValue, isGlobal, formValues) => {
|
1385
1443
|
const isArrayNames = Array.isArray(fieldNames);
|
1386
|
-
const fieldValues =
|
1387
|
-
? Object.assign(Object.assign({}, defaultValuesRef.current), (formValues ||
|
1444
|
+
const fieldValues = formValues || isMountedRef.current
|
1445
|
+
? Object.assign(Object.assign({}, defaultValuesRef.current), (formValues || getFieldsValues(fieldsRef))) : isUndefined(defaultValue)
|
1388
1446
|
? defaultValuesRef.current
|
1389
1447
|
: isArrayNames
|
1390
1448
|
? defaultValue
|
1391
|
-
: { [fieldNames]: defaultValue }
|
1449
|
+
: { [fieldNames]: defaultValue };
|
1392
1450
|
if (isUndefined(fieldNames)) {
|
1393
1451
|
isGlobal && (namesRef.current.watchAll = true);
|
1394
1452
|
return fieldValues;
|
@@ -1398,12 +1456,7 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1398
1456
|
isGlobal && namesRef.current.watch.add(fieldName);
|
1399
1457
|
result.push(get(fieldValues, fieldName));
|
1400
1458
|
}
|
1401
|
-
return isArrayNames
|
1402
|
-
? result
|
1403
|
-
: isObject(result[0])
|
1404
|
-
? Object.assign({}, result[0]) : Array.isArray(result[0])
|
1405
|
-
? [...result[0]]
|
1406
|
-
: result[0];
|
1459
|
+
return isArrayNames ? result : result[0];
|
1407
1460
|
}, []);
|
1408
1461
|
const watch = (fieldName, defaultValue) => isFunction(fieldName)
|
1409
1462
|
? subjectsRef.current.watch.subscribe({
|
@@ -1417,11 +1470,8 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1417
1470
|
namesRef.current.mount.delete(inputName);
|
1418
1471
|
namesRef.current.array.delete(inputName);
|
1419
1472
|
if (get(fieldsRef.current, inputName)) {
|
1420
|
-
if (!options.keepValue) {
|
1421
|
-
unset(fieldsRef.current, inputName);
|
1422
|
-
unset(_formValues.current, inputName);
|
1423
|
-
}
|
1424
1473
|
!options.keepError && unset(formStateRef.current.errors, inputName);
|
1474
|
+
!options.keepValue && unset(fieldsRef.current, inputName);
|
1425
1475
|
!options.keepDirty &&
|
1426
1476
|
unset(formStateRef.current.dirtyFields, inputName);
|
1427
1477
|
!options.keepTouched &&
|
@@ -1461,22 +1511,15 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1461
1511
|
set(fieldsRef.current, name, {
|
1462
1512
|
_f: Object.assign(Object.assign(Object.assign({}, (field && field._f ? field._f : { ref: { name } })), { name, mount: true }), options),
|
1463
1513
|
});
|
1464
|
-
if (options.value) {
|
1465
|
-
set(_formValues.current, name, options.value);
|
1466
|
-
}
|
1467
|
-
if (!isUndefined(options.disabled) &&
|
1468
|
-
field &&
|
1469
|
-
field._f &&
|
1470
|
-
field._f.ref.disabled !== options.disabled) {
|
1471
|
-
set(_formValues.current, name, options.disabled ? undefined : field._f.ref.value);
|
1472
|
-
}
|
1473
1514
|
namesRef.current.mount.add(name);
|
1474
|
-
!field && updateIsValidAndInputValue(name);
|
1515
|
+
!field && updateIsValidAndInputValue(name, undefined, true);
|
1475
1516
|
return isWindowUndefined
|
1476
1517
|
? { name: name }
|
1477
|
-
:
|
1478
|
-
|
1479
|
-
|
1518
|
+
: {
|
1519
|
+
name,
|
1520
|
+
onChange: handleChange,
|
1521
|
+
onBlur: handleChange,
|
1522
|
+
ref: (ref) => {
|
1480
1523
|
if (ref) {
|
1481
1524
|
registerFieldRef(name, ref, options);
|
1482
1525
|
}
|
@@ -1485,13 +1528,20 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1485
1528
|
const shouldUnmount = shouldUnregister || options.shouldUnregister;
|
1486
1529
|
if (field._f) {
|
1487
1530
|
field._f.mount = false;
|
1531
|
+
// If initial state of field element is disabled,
|
1532
|
+
// value is not set on first "register"
|
1533
|
+
// re-sync the value in when it switched to enabled
|
1534
|
+
if (isUndefined(field._f.value)) {
|
1535
|
+
field._f.value = field._f.ref.value;
|
1536
|
+
}
|
1488
1537
|
}
|
1489
1538
|
shouldUnmount &&
|
1490
1539
|
!(isNameInFieldArray(namesRef.current.array, name) &&
|
1491
1540
|
inFieldArrayActionRef.current) &&
|
1492
1541
|
namesRef.current.unMount.add(name);
|
1493
1542
|
}
|
1494
|
-
}
|
1543
|
+
},
|
1544
|
+
};
|
1495
1545
|
}, []);
|
1496
1546
|
const handleSubmit = React.useCallback((onValid, onInvalid) => async (e) => {
|
1497
1547
|
if (e) {
|
@@ -1499,7 +1549,7 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1499
1549
|
e.persist && e.persist();
|
1500
1550
|
}
|
1501
1551
|
let hasNoPromiseError = true;
|
1502
|
-
let fieldValues =
|
1552
|
+
let fieldValues = getFieldsValues(fieldsRef);
|
1503
1553
|
subjectsRef.current.state.next({
|
1504
1554
|
isSubmitting: true,
|
1505
1555
|
});
|
@@ -1584,7 +1634,6 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1584
1634
|
}
|
1585
1635
|
if (!keepStateOptions.keepValues) {
|
1586
1636
|
fieldsRef.current = {};
|
1587
|
-
_formValues.current = {};
|
1588
1637
|
subjectsRef.current.control.next({
|
1589
1638
|
values: keepStateOptions.keepDefaultValues
|
1590
1639
|
? defaultValuesRef.current
|
@@ -1642,8 +1691,9 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1642
1691
|
const useFieldArraySubscription = subjectsRef.current.array.subscribe({
|
1643
1692
|
next(state) {
|
1644
1693
|
if (state.values && state.name && readFormStateRef.current.isValid) {
|
1645
|
-
|
1646
|
-
|
1694
|
+
const values = getFieldsValues(fieldsRef);
|
1695
|
+
set(values, state.name, state.values);
|
1696
|
+
updateIsValid(values);
|
1647
1697
|
}
|
1648
1698
|
},
|
1649
1699
|
});
|
@@ -1687,7 +1737,6 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
|
|
1687
1737
|
defaultValuesRef,
|
1688
1738
|
fieldArrayDefaultValuesRef,
|
1689
1739
|
setValues,
|
1690
|
-
_formValues,
|
1691
1740
|
unregister,
|
1692
1741
|
shouldUnmount: shouldUnregister,
|
1693
1742
|
}), []),
|