react-hook-form 8.0.0-beta.2 → 8.0.0-beta.3
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/README.md +0 -11
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/fieldArray.d.ts +36 -0
- package/dist/fieldArray.d.ts.map +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.mjs +420 -200
- package/dist/index.esm.mjs.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/logic/createFormControl.d.ts.map +1 -1
- package/dist/logic/getNodeParentName.d.ts.map +1 -1
- package/dist/logic/hasPromiseValidation.d.ts.map +1 -1
- package/dist/logic/index.d.ts +2 -3
- package/dist/logic/index.d.ts.map +1 -1
- package/dist/logic/{getFieldArrayParentNames.d.ts → isNameInFieldArray.d.ts} +2 -2
- package/dist/logic/isNameInFieldArray.d.ts.map +1 -0
- package/dist/logic/isWatched.d.ts.map +1 -1
- package/dist/logic/shouldRenderFormState.d.ts.map +1 -1
- package/dist/logic/skipValidation.d.ts +1 -4
- package/dist/logic/skipValidation.d.ts.map +1 -1
- package/dist/logic/updateFieldArrayRootError.d.ts.map +1 -1
- package/dist/logic/validateField.d.ts.map +1 -1
- package/dist/react-server.esm.mjs +265 -121
- package/dist/react-server.esm.mjs.map +1 -1
- package/dist/types/fieldArray.d.ts +25 -1
- package/dist/types/fieldArray.d.ts.map +1 -1
- package/dist/types/form.d.ts +49 -3
- package/dist/types/form.d.ts.map +1 -1
- package/dist/types/utils.d.ts +1 -1
- package/dist/types/utils.d.ts.map +1 -1
- package/dist/useController.d.ts.map +1 -1
- package/dist/useFieldArray.d.ts.map +1 -1
- package/dist/useForm.d.ts.map +1 -1
- package/dist/useFormContext.d.ts +1 -1
- package/dist/useFormContext.d.ts.map +1 -1
- package/dist/useIsomorphicLayoutEffect.d.ts.map +1 -1
- package/dist/utils/deepEqual.d.ts +1 -1
- package/dist/utils/deepEqual.d.ts.map +1 -1
- package/dist/utils/flatten.d.ts.map +1 -1
- package/dist/utils/get.d.ts.map +1 -1
- package/dist/utils/index.d.ts +2 -3
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/isKey.d.ts.map +1 -1
- package/dist/utils/set.d.ts.map +1 -1
- package/dist/utils/stringToPath.d.ts.map +1 -1
- package/dist/utils/unset.d.ts.map +1 -1
- package/package.json +23 -19
- package/dist/logic/getFieldArrayParentNames.d.ts.map +0 -1
package/dist/index.esm.mjs
CHANGED
|
@@ -18,15 +18,9 @@ var getEventValue = (event) => isObject(event) && event.target
|
|
|
18
18
|
: event.target.value
|
|
19
19
|
: event;
|
|
20
20
|
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
let prefix = parts[0];
|
|
25
|
-
for (let i = 1; i < parts.length; prefix += '.' + parts[i++]) {
|
|
26
|
-
!isNaN(+parts[i]) && names.has(prefix) && matches.push(prefix);
|
|
27
|
-
}
|
|
28
|
-
return matches;
|
|
29
|
-
};
|
|
21
|
+
var isNameInFieldArray = (names, name) => name
|
|
22
|
+
.split('.')
|
|
23
|
+
.some((part, index, arr) => !isNaN(Number(part)) && names.has(arr.slice(0, index).join('.')));
|
|
30
24
|
|
|
31
25
|
var isPlainObject = (tempObject) => {
|
|
32
26
|
const prototypeCopy = tempObject.constructor && tempObject.constructor.prototype;
|
|
@@ -58,19 +52,49 @@ function cloneObject(data) {
|
|
|
58
52
|
return copy;
|
|
59
53
|
}
|
|
60
54
|
|
|
61
|
-
|
|
55
|
+
const EVENTS = {
|
|
56
|
+
BLUR: 'blur',
|
|
57
|
+
FOCUS_OUT: 'focusout',
|
|
58
|
+
CHANGE: 'change',
|
|
59
|
+
SUBMIT: 'submit',
|
|
60
|
+
TRIGGER: 'trigger',
|
|
61
|
+
VALID: 'valid',
|
|
62
|
+
};
|
|
63
|
+
const VALIDATION_MODE = {
|
|
64
|
+
onBlur: 'onBlur',
|
|
65
|
+
onChange: 'onChange',
|
|
66
|
+
onSubmit: 'onSubmit',
|
|
67
|
+
onTouched: 'onTouched',
|
|
68
|
+
all: 'all',
|
|
69
|
+
};
|
|
70
|
+
const INPUT_VALIDATION_RULES = {
|
|
71
|
+
max: 'max',
|
|
72
|
+
min: 'min',
|
|
73
|
+
maxLength: 'maxLength',
|
|
74
|
+
minLength: 'minLength',
|
|
75
|
+
pattern: 'pattern',
|
|
76
|
+
required: 'required',
|
|
77
|
+
validate: 'validate',
|
|
78
|
+
};
|
|
79
|
+
const ROOT_ERROR_TYPE = 'root';
|
|
80
|
+
const PROTOTYPE_KEYWORDS = ['__proto__', 'constructor', 'prototype'];
|
|
81
|
+
|
|
82
|
+
const IS_KEY_RE = /^\w*$/;
|
|
83
|
+
var isKey = (value) => IS_KEY_RE.test(value);
|
|
62
84
|
|
|
63
85
|
var isUndefined = (val) => val === undefined;
|
|
64
86
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
var stringToPath = (input) => compact(input.replace(/["|']|\]/g, '').split(/\.|\[/));
|
|
87
|
+
const FIELD_PATH_RE = /[.[\]'"]/;
|
|
88
|
+
var stringToPath = (input) => input.split(FIELD_PATH_RE).filter(Boolean);
|
|
68
89
|
|
|
69
90
|
var get = (object, path, defaultValue) => {
|
|
70
91
|
if (!path || !isObject(object)) {
|
|
71
92
|
return defaultValue;
|
|
72
93
|
}
|
|
73
94
|
const paths = isKey(path) ? [path] : stringToPath(path);
|
|
95
|
+
if (paths.some((key) => PROTOTYPE_KEYWORDS.includes(key))) {
|
|
96
|
+
return defaultValue;
|
|
97
|
+
}
|
|
74
98
|
const result = paths.reduce((result, key) => {
|
|
75
99
|
return isNullOrUndefined(result) ? undefined : result[key];
|
|
76
100
|
}, object);
|
|
@@ -100,7 +124,7 @@ var set = (object, path, value) => {
|
|
|
100
124
|
? []
|
|
101
125
|
: {};
|
|
102
126
|
}
|
|
103
|
-
if (key
|
|
127
|
+
if (PROTOTYPE_KEYWORDS.includes(key)) {
|
|
104
128
|
return;
|
|
105
129
|
}
|
|
106
130
|
object[key] = newValue;
|
|
@@ -108,33 +132,6 @@ var set = (object, path, value) => {
|
|
|
108
132
|
}
|
|
109
133
|
};
|
|
110
134
|
|
|
111
|
-
const EVENTS = {
|
|
112
|
-
BLUR: 'blur',
|
|
113
|
-
FOCUS_OUT: 'focusout',
|
|
114
|
-
CHANGE: 'change',
|
|
115
|
-
SUBMIT: 'submit',
|
|
116
|
-
TRIGGER: 'trigger',
|
|
117
|
-
VALID: 'valid',
|
|
118
|
-
};
|
|
119
|
-
const VALIDATION_MODE = {
|
|
120
|
-
onBlur: 'onBlur',
|
|
121
|
-
onChange: 'onChange',
|
|
122
|
-
onSubmit: 'onSubmit',
|
|
123
|
-
onTouched: 'onTouched',
|
|
124
|
-
all: 'all',
|
|
125
|
-
};
|
|
126
|
-
const INPUT_VALIDATION_RULES = {
|
|
127
|
-
max: 'max',
|
|
128
|
-
min: 'min',
|
|
129
|
-
maxLength: 'maxLength',
|
|
130
|
-
minLength: 'minLength',
|
|
131
|
-
pattern: 'pattern',
|
|
132
|
-
required: 'required',
|
|
133
|
-
validate: 'validate',
|
|
134
|
-
};
|
|
135
|
-
const FORM_ERROR_TYPE = 'form';
|
|
136
|
-
const ROOT_ERROR_TYPE = 'root';
|
|
137
|
-
|
|
138
135
|
/**
|
|
139
136
|
* Separate context for `control` to prevent unnecessary rerenders.
|
|
140
137
|
* Internal hooks that only need control use this instead of full form context.
|
|
@@ -163,7 +160,9 @@ var getProxyFormState = (formState, control, localProxyFormState, isRoot = true)
|
|
|
163
160
|
return result;
|
|
164
161
|
};
|
|
165
162
|
|
|
166
|
-
const useIsomorphicLayoutEffect =
|
|
163
|
+
const useIsomorphicLayoutEffect = isWeb
|
|
164
|
+
? React.useLayoutEffect
|
|
165
|
+
: React.useEffect;
|
|
167
166
|
|
|
168
167
|
/**
|
|
169
168
|
* This custom hook allows you to subscribe to each form state, and isolate the re-render at the custom hook level. It has its scope in terms of form state subscription, so it would not affect other useFormState and useForm. Using this hook can reduce the re-render impact on large and complex form application.
|
|
@@ -248,7 +247,8 @@ var generateWatchOutput = (names, _names, formValues, isGlobal, defaultValue) =>
|
|
|
248
247
|
|
|
249
248
|
var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
|
|
250
249
|
|
|
251
|
-
|
|
250
|
+
const isEmptyObjectWithCustomPrototype = (object, keys) => keys.length === 0 && !Array.isArray(object) && !isPlainObject(object);
|
|
251
|
+
function deepEqual(object1, object2, visited = new WeakMap()) {
|
|
252
252
|
if (object1 === object2) {
|
|
253
253
|
return true;
|
|
254
254
|
}
|
|
@@ -263,11 +263,25 @@ function deepEqual(object1, object2, visited = new WeakSet()) {
|
|
|
263
263
|
if (keys1.length !== keys2.length) {
|
|
264
264
|
return false;
|
|
265
265
|
}
|
|
266
|
-
if (
|
|
266
|
+
if (isEmptyObjectWithCustomPrototype(object1, keys1) ||
|
|
267
|
+
isEmptyObjectWithCustomPrototype(object2, keys2)) {
|
|
268
|
+
return Object.is(object1, object2);
|
|
269
|
+
}
|
|
270
|
+
if (!keys1.length && Array.isArray(object1) !== Array.isArray(object2)) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
const visitedPairs = visited.get(object1);
|
|
274
|
+
if (visitedPairs && visitedPairs.has(object2)) {
|
|
267
275
|
return true;
|
|
268
276
|
}
|
|
269
|
-
|
|
270
|
-
|
|
277
|
+
if (visitedPairs) {
|
|
278
|
+
visitedPairs.add(object2);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
const ws = new WeakSet();
|
|
282
|
+
ws.add(object2);
|
|
283
|
+
visited.set(object1, ws);
|
|
284
|
+
}
|
|
271
285
|
for (const key of keys1) {
|
|
272
286
|
const val1 = object1[key];
|
|
273
287
|
if (!(key in object2)) {
|
|
@@ -400,8 +414,7 @@ function useWatch(props) {
|
|
|
400
414
|
function useController(props) {
|
|
401
415
|
const formControl = useFormControlContext();
|
|
402
416
|
const { name, disabled, control = formControl, shouldUnregister, defaultValue, exact = true, } = props;
|
|
403
|
-
const isArrayField =
|
|
404
|
-
.length;
|
|
417
|
+
const isArrayField = isNameInFieldArray(control._names.array, name);
|
|
405
418
|
const defaultValueMemo = React.useMemo(() => get(control._formValues, name, get(control._defaultValues, name, defaultValue)), [control, name, defaultValue]);
|
|
406
419
|
const value = useWatch({
|
|
407
420
|
control,
|
|
@@ -415,6 +428,7 @@ function useController(props) {
|
|
|
415
428
|
exact,
|
|
416
429
|
});
|
|
417
430
|
const _props = React.useRef(props);
|
|
431
|
+
const _proxyRef = React.useRef(null);
|
|
418
432
|
const _registerProps = React.useRef(control.register(name, {
|
|
419
433
|
...props.rules,
|
|
420
434
|
value,
|
|
@@ -443,13 +457,22 @@ function useController(props) {
|
|
|
443
457
|
get: () => get(formState.errors, name),
|
|
444
458
|
},
|
|
445
459
|
}), [formState, name]);
|
|
446
|
-
const onChange = React.useCallback((event) =>
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
460
|
+
const onChange = React.useCallback((event) => {
|
|
461
|
+
const value = getEventValue(event);
|
|
462
|
+
if (!get(control._fields, name)) {
|
|
463
|
+
_registerProps.current = control.register(name, {
|
|
464
|
+
..._props.current.rules,
|
|
465
|
+
value,
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
return _registerProps.current.onChange({
|
|
469
|
+
target: {
|
|
470
|
+
value: getEventValue(event),
|
|
471
|
+
name: name,
|
|
472
|
+
},
|
|
473
|
+
type: EVENTS.CHANGE,
|
|
474
|
+
});
|
|
475
|
+
}, [name, control]);
|
|
453
476
|
const onBlur = React.useCallback(() => _registerProps.current.onBlur({
|
|
454
477
|
target: {
|
|
455
478
|
value: get(control._formValues, name),
|
|
@@ -458,9 +481,12 @@ function useController(props) {
|
|
|
458
481
|
type: EVENTS.BLUR,
|
|
459
482
|
}), [name, control._formValues]);
|
|
460
483
|
const ref = React.useCallback((elm) => {
|
|
484
|
+
if (elm) {
|
|
485
|
+
_proxyRef.current = elm;
|
|
486
|
+
}
|
|
461
487
|
const field = get(control._fields, name);
|
|
462
488
|
if (field && field._f && elm) {
|
|
463
|
-
field._f.ref =
|
|
489
|
+
field._f.ref = _proxyRef.current;
|
|
464
490
|
}
|
|
465
491
|
}, [control._fields, name]);
|
|
466
492
|
const field = React.useMemo(() => ({
|
|
@@ -489,13 +515,21 @@ function useController(props) {
|
|
|
489
515
|
};
|
|
490
516
|
updateMounted(name, true);
|
|
491
517
|
if (_shouldUnregisterField) {
|
|
492
|
-
const value = cloneObject(get(
|
|
518
|
+
const value = cloneObject(get(shouldUnregister
|
|
519
|
+
? control._defaultValues
|
|
520
|
+
: control._options.values || control._defaultValues, name, get(control._options.defaultValues, name, _props.current.defaultValue)));
|
|
493
521
|
set(control._defaultValues, name, value);
|
|
494
522
|
if (isUndefined(get(control._formValues, name))) {
|
|
495
523
|
set(control._formValues, name, value);
|
|
496
524
|
}
|
|
497
525
|
}
|
|
498
526
|
!isArrayField && control.register(name);
|
|
527
|
+
if (_proxyRef.current) {
|
|
528
|
+
const field = get(control._fields, name);
|
|
529
|
+
if (field && field._f) {
|
|
530
|
+
field._f.ref = _proxyRef.current;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
499
533
|
return () => {
|
|
500
534
|
(isArrayField
|
|
501
535
|
? _shouldUnregisterField && !control._state.action
|
|
@@ -564,7 +598,9 @@ const Controller = (props) => props.render(useController(props));
|
|
|
564
598
|
const flatten = (obj) => {
|
|
565
599
|
const output = {};
|
|
566
600
|
for (const key of Object.keys(obj)) {
|
|
567
|
-
if (isObjectType(obj[key]) &&
|
|
601
|
+
if (isObjectType(obj[key]) &&
|
|
602
|
+
obj[key] !== null &&
|
|
603
|
+
!isDateObject(obj[key])) {
|
|
568
604
|
const nested = flatten(obj[key]);
|
|
569
605
|
for (const nestedKey of Object.keys(nested)) {
|
|
570
606
|
output[`${key}.${nestedKey}`] = nested[nestedKey];
|
|
@@ -640,8 +676,7 @@ const useFormContext = () => React.useContext(HookFormContext);
|
|
|
640
676
|
* }
|
|
641
677
|
* ```
|
|
642
678
|
*/
|
|
643
|
-
const FormProvider = (
|
|
644
|
-
const { children, watch, getValues, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, handleSubmit, unregister, control, register, setFocus, subscribe, } = props;
|
|
679
|
+
const FormProvider = ({ children, watch, getValues, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, handleSubmit, unregister, control, register, setFocus, subscribe, }) => {
|
|
645
680
|
const memoizedValue = React.useMemo(() => ({
|
|
646
681
|
watch,
|
|
647
682
|
getValues,
|
|
@@ -806,6 +841,8 @@ var appendErrors = (name, validateAllFieldCriteria, errors, type, message) => va
|
|
|
806
841
|
}
|
|
807
842
|
: {};
|
|
808
843
|
|
|
844
|
+
var compact = (value) => Array.isArray(value) ? value.filter(Boolean) : [];
|
|
845
|
+
|
|
809
846
|
var convertToArrayPayload = (value) => (Array.isArray(value) ? value : [value]);
|
|
810
847
|
|
|
811
848
|
var createSubject = () => {
|
|
@@ -910,6 +947,9 @@ function unset(object, path) {
|
|
|
910
947
|
: isKey(path)
|
|
911
948
|
? [path]
|
|
912
949
|
: stringToPath(path);
|
|
950
|
+
if (paths.some((segment) => PROTOTYPE_KEYWORDS.includes(String(segment)))) {
|
|
951
|
+
return object;
|
|
952
|
+
}
|
|
913
953
|
const childObject = paths.length === 1 ? object : baseGet(object, paths);
|
|
914
954
|
const index = paths.length - 1;
|
|
915
955
|
const key = paths[index];
|
|
@@ -1098,12 +1138,22 @@ var getValidationModes = (mode) => ({
|
|
|
1098
1138
|
});
|
|
1099
1139
|
|
|
1100
1140
|
const ASYNC_FUNCTION = 'AsyncFunction';
|
|
1101
|
-
var hasPromiseValidation = (fieldReference) =>
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1141
|
+
var hasPromiseValidation = (fieldReference) => {
|
|
1142
|
+
if (!fieldReference || !fieldReference.validate)
|
|
1143
|
+
return false;
|
|
1144
|
+
if (isFunction(fieldReference.validate)) {
|
|
1145
|
+
return fieldReference.validate.constructor.name === ASYNC_FUNCTION;
|
|
1146
|
+
}
|
|
1147
|
+
if (isObject(fieldReference.validate)) {
|
|
1148
|
+
for (const key in fieldReference.validate) {
|
|
1149
|
+
if (fieldReference.validate[key].constructor
|
|
1150
|
+
.name === ASYNC_FUNCTION) {
|
|
1151
|
+
return true;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
return false;
|
|
1156
|
+
};
|
|
1107
1157
|
|
|
1108
1158
|
var hasValidation = (options) => options.mount &&
|
|
1109
1159
|
(options.required ||
|
|
@@ -1114,11 +1164,17 @@ var hasValidation = (options) => options.mount &&
|
|
|
1114
1164
|
options.pattern ||
|
|
1115
1165
|
options.validate);
|
|
1116
1166
|
|
|
1117
|
-
var isWatched = (name, _names, isBlurEvent) =>
|
|
1118
|
-
(
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1167
|
+
var isWatched = (name, _names, isBlurEvent) => {
|
|
1168
|
+
if (isBlurEvent)
|
|
1169
|
+
return false;
|
|
1170
|
+
if (_names.watchAll || _names.watch.has(name))
|
|
1171
|
+
return true;
|
|
1172
|
+
for (const watchName of _names.watch) {
|
|
1173
|
+
if (name.startsWith(watchName) && name.charAt(watchName.length) === '.')
|
|
1174
|
+
return true;
|
|
1175
|
+
}
|
|
1176
|
+
return false;
|
|
1177
|
+
};
|
|
1122
1178
|
|
|
1123
1179
|
const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
|
|
1124
1180
|
for (const key of fieldsNames || Object.keys(fields)) {
|
|
@@ -1186,10 +1242,10 @@ function schemaErrorLookup(errors, _fields, name) {
|
|
|
1186
1242
|
var shouldRenderFormState = (formStateData, _proxyFormState, updateFormState, isRoot) => {
|
|
1187
1243
|
updateFormState(formStateData);
|
|
1188
1244
|
const { name, ...formState } = formStateData;
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1245
|
+
const keys = Object.keys(formState);
|
|
1246
|
+
return (!keys.length ||
|
|
1247
|
+
(isRoot && keys.length >= Object.keys(_proxyFormState).length) ||
|
|
1248
|
+
keys.find((key) => _proxyFormState[key] ===
|
|
1193
1249
|
(!isRoot || VALIDATION_MODE.all)));
|
|
1194
1250
|
};
|
|
1195
1251
|
|
|
@@ -1198,7 +1254,7 @@ var shouldSubscribeByName = (name, signalName, exact) => !name ||
|
|
|
1198
1254
|
name === signalName ||
|
|
1199
1255
|
convertToArrayPayload(name).some((currentName) => currentName &&
|
|
1200
1256
|
(exact
|
|
1201
|
-
? currentName === signalName
|
|
1257
|
+
? currentName === signalName || currentName.startsWith(signalName + '.')
|
|
1202
1258
|
: currentName.startsWith(signalName) ||
|
|
1203
1259
|
signalName.startsWith(currentName)));
|
|
1204
1260
|
|
|
@@ -1221,7 +1277,8 @@ var skipValidation = (isBlurEvent, isTouched, isSubmitted, reValidateMode, mode)
|
|
|
1221
1277
|
var unsetEmptyArray = (ref, name) => !compact(get(ref, name)).length && unset(ref, name);
|
|
1222
1278
|
|
|
1223
1279
|
var updateFieldArrayRootError = (errors, error, name) => {
|
|
1224
|
-
const
|
|
1280
|
+
const existingErrors = get(errors, name);
|
|
1281
|
+
const fieldArrayErrors = Array.isArray(existingErrors) ? existingErrors : [];
|
|
1225
1282
|
set(fieldArrayErrors, ROOT_ERROR_TYPE, error[name]);
|
|
1226
1283
|
set(errors, name, fieldArrayErrors);
|
|
1227
1284
|
return errors;
|
|
@@ -1255,7 +1312,13 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
|
|
|
1255
1312
|
const inputRef = refs ? refs[0] : ref;
|
|
1256
1313
|
const setCustomValidity = (message) => {
|
|
1257
1314
|
if (shouldUseNativeValidation && inputRef.reportValidity) {
|
|
1258
|
-
|
|
1315
|
+
const validityMessage = isBoolean(message) ? '' : message || '';
|
|
1316
|
+
if (refs) {
|
|
1317
|
+
refs.forEach((ref) => ref.setCustomValidity(validityMessage));
|
|
1318
|
+
}
|
|
1319
|
+
else {
|
|
1320
|
+
inputRef.setCustomValidity(validityMessage);
|
|
1321
|
+
}
|
|
1259
1322
|
inputRef.reportValidity();
|
|
1260
1323
|
}
|
|
1261
1324
|
};
|
|
@@ -1268,8 +1331,7 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
|
|
|
1268
1331
|
isUndefined(inputValue)) ||
|
|
1269
1332
|
(isHTMLElement(ref) && ref.value === '') ||
|
|
1270
1333
|
inputValue === '' ||
|
|
1271
|
-
(Array.isArray(inputValue) && !inputValue.length)
|
|
1272
|
-
(valueAsNumber && typeof inputValue === 'number' && isNaN(inputValue));
|
|
1334
|
+
(Array.isArray(inputValue) && !inputValue.length);
|
|
1273
1335
|
const appendErrorsCurry = appendErrors.bind(null, name, validateAllFieldCriteria, error);
|
|
1274
1336
|
const getMinMaxMessage = (exceedMax, maxLengthMessage, minLengthMessage, maxType = INPUT_VALIDATION_RULES.maxLength, minType = INPUT_VALIDATION_RULES.minLength) => {
|
|
1275
1337
|
const message = exceedMax ? maxLengthMessage : minLengthMessage;
|
|
@@ -1431,6 +1493,7 @@ const defaultOptions = {
|
|
|
1431
1493
|
reValidateMode: VALIDATION_MODE.onChange,
|
|
1432
1494
|
shouldFocusError: true,
|
|
1433
1495
|
};
|
|
1496
|
+
const FORM_ERROR_TYPE = 'form';
|
|
1434
1497
|
const DEFAULT_FORM_STATE = {
|
|
1435
1498
|
submitCount: 0,
|
|
1436
1499
|
isDirty: false,
|
|
@@ -1478,6 +1541,9 @@ function createFormControl(props = {}) {
|
|
|
1478
1541
|
};
|
|
1479
1542
|
let delayErrorCallback;
|
|
1480
1543
|
let timer = 0;
|
|
1544
|
+
let _valuesSubscriberCount = 0;
|
|
1545
|
+
let _validationModeBeforeSubmit = getValidationModes(_options.mode);
|
|
1546
|
+
let _validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
|
|
1481
1547
|
const defaultProxyFormState = {
|
|
1482
1548
|
isDirty: false,
|
|
1483
1549
|
dirtyFields: false,
|
|
@@ -1588,6 +1654,7 @@ function createFormControl(props = {}) {
|
|
|
1588
1654
|
};
|
|
1589
1655
|
const updateErrors = (name, error) => {
|
|
1590
1656
|
set(_formState.errors, name, error);
|
|
1657
|
+
_formState.errors = { ..._formState.errors };
|
|
1591
1658
|
_subjects.state.next({
|
|
1592
1659
|
errors: _formState.errors,
|
|
1593
1660
|
});
|
|
@@ -1630,12 +1697,6 @@ function createFormControl(props = {}) {
|
|
|
1630
1697
|
: setFieldValue(name, defaultValue);
|
|
1631
1698
|
if (_state.mount && !_state.action) {
|
|
1632
1699
|
_setValid();
|
|
1633
|
-
// Re-registering a field after a prior unregister puts its key back
|
|
1634
|
-
// into _formValues, which can flip isDirty back to false (#13397).
|
|
1635
|
-
// Only run when we are currently dirty, otherwise an initial register
|
|
1636
|
-
// for a field with no defaultValue would flip isDirty to true. Reset
|
|
1637
|
-
// paths repopulate _formValues before re-register, so the key is
|
|
1638
|
-
// present then and this branch is skipped (preserves keepDirty).
|
|
1639
1700
|
if (wasUnsetInFormValues &&
|
|
1640
1701
|
_formState.isDirty &&
|
|
1641
1702
|
(_proxyFormState.isDirty || _proxySubscribeFormState.isDirty)) {
|
|
@@ -1645,6 +1706,12 @@ function createFormControl(props = {}) {
|
|
|
1645
1706
|
_subjects.state.next({ ..._formState });
|
|
1646
1707
|
}
|
|
1647
1708
|
}
|
|
1709
|
+
if (props.shouldUnregister &&
|
|
1710
|
+
wasUnsetInFormValues &&
|
|
1711
|
+
!isUndefined(get(_formValues, name)) &&
|
|
1712
|
+
isWatched(name, _names)) {
|
|
1713
|
+
_state.watch = true;
|
|
1714
|
+
}
|
|
1648
1715
|
}
|
|
1649
1716
|
}
|
|
1650
1717
|
};
|
|
@@ -1656,12 +1723,13 @@ function createFormControl(props = {}) {
|
|
|
1656
1723
|
};
|
|
1657
1724
|
if (!_options.disabled) {
|
|
1658
1725
|
if (!isBlurEvent || shouldDirty) {
|
|
1726
|
+
const isCurrentFieldPristine = deepEqual(get(_defaultValues, name), fieldValue);
|
|
1659
1727
|
if (_proxyFormState.isDirty || _proxySubscribeFormState.isDirty) {
|
|
1660
1728
|
isPreviousDirty = _formState.isDirty;
|
|
1661
|
-
_formState.isDirty = output.isDirty =
|
|
1729
|
+
_formState.isDirty = output.isDirty =
|
|
1730
|
+
!isCurrentFieldPristine || _getDirty();
|
|
1662
1731
|
shouldUpdateField = isPreviousDirty !== output.isDirty;
|
|
1663
1732
|
}
|
|
1664
|
-
const isCurrentFieldPristine = deepEqual(get(_defaultValues, name), fieldValue);
|
|
1665
1733
|
isPreviousDirty = !!get(_formState.dirtyFields, name);
|
|
1666
1734
|
if (isCurrentFieldPristine !== _formState.isDirty) {
|
|
1667
1735
|
_formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
|
|
@@ -1709,6 +1777,7 @@ function createFormControl(props = {}) {
|
|
|
1709
1777
|
error
|
|
1710
1778
|
? set(_formState.errors, name, error)
|
|
1711
1779
|
: unset(_formState.errors, name);
|
|
1780
|
+
_formState.errors = { ..._formState.errors };
|
|
1712
1781
|
}
|
|
1713
1782
|
if ((error ? !deepEqual(previousFieldError, error) : previousFieldError) ||
|
|
1714
1783
|
!isEmptyObject(fieldState) ||
|
|
@@ -1737,11 +1806,14 @@ function createFormControl(props = {}) {
|
|
|
1737
1806
|
for (const name of names) {
|
|
1738
1807
|
const error = get(errors, name);
|
|
1739
1808
|
error
|
|
1740
|
-
? _names.array.has(name) &&
|
|
1809
|
+
? _names.array.has(name) &&
|
|
1810
|
+
isObject(error) &&
|
|
1811
|
+
!Object.keys(error).some((key) => !Number.isNaN(Number(key)))
|
|
1741
1812
|
? updateFieldArrayRootError(_formState.errors, { [name]: error }, name)
|
|
1742
1813
|
: set(_formState.errors, name, error)
|
|
1743
1814
|
: unset(_formState.errors, name);
|
|
1744
1815
|
}
|
|
1816
|
+
_formState.errors = { ..._formState.errors };
|
|
1745
1817
|
}
|
|
1746
1818
|
else {
|
|
1747
1819
|
_formState.errors = errors;
|
|
@@ -1856,7 +1928,7 @@ function createFormControl(props = {}) {
|
|
|
1856
1928
|
};
|
|
1857
1929
|
const _getDirty = (name, data) => !_options.disabled &&
|
|
1858
1930
|
(name && data && set(_formValues, name, data),
|
|
1859
|
-
!deepEqual(
|
|
1931
|
+
!deepEqual(_state.mount ? _formValues : _defaultValues, _defaultValues));
|
|
1860
1932
|
const _getWatch = (names, defaultValue, isGlobal) => generateWatchOutput(names, _names, {
|
|
1861
1933
|
...(_state.mount
|
|
1862
1934
|
? _formValues
|
|
@@ -1867,7 +1939,7 @@ function createFormControl(props = {}) {
|
|
|
1867
1939
|
: defaultValue),
|
|
1868
1940
|
}, isGlobal, defaultValue);
|
|
1869
1941
|
const _getFieldArray = (name) => compact(get(_state.mount ? _formValues : _defaultValues, name, _options.shouldUnregister ? get(_defaultValues, name, []) : []));
|
|
1870
|
-
const setFieldValue = (name, value, options = {}) => {
|
|
1942
|
+
const setFieldValue = (name, value, options = {}, skipClone = false, skipRender = false) => {
|
|
1871
1943
|
const field = get(_fields, name);
|
|
1872
1944
|
let fieldValue = value;
|
|
1873
1945
|
if (field) {
|
|
@@ -1905,20 +1977,20 @@ function createFormControl(props = {}) {
|
|
|
1905
1977
|
}
|
|
1906
1978
|
else {
|
|
1907
1979
|
fieldReference.ref.value = fieldValue;
|
|
1908
|
-
if (!fieldReference.ref.type) {
|
|
1980
|
+
if (!fieldReference.ref.type && !skipRender) {
|
|
1909
1981
|
_subjects.state.next({
|
|
1910
1982
|
name,
|
|
1911
|
-
values: cloneObject(_formValues),
|
|
1983
|
+
values: skipClone ? _formValues : cloneObject(_formValues),
|
|
1912
1984
|
});
|
|
1913
1985
|
}
|
|
1914
1986
|
}
|
|
1915
1987
|
}
|
|
1916
1988
|
}
|
|
1917
1989
|
(options.shouldDirty || options.shouldTouch) &&
|
|
1918
|
-
updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty,
|
|
1990
|
+
updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty, !skipRender);
|
|
1919
1991
|
options.shouldValidate && trigger(name);
|
|
1920
1992
|
};
|
|
1921
|
-
const setFieldValues = (name, value, options) => {
|
|
1993
|
+
const setFieldValues = (name, value, options, skipClone = false, skipRender = false) => {
|
|
1922
1994
|
for (const fieldKey in value) {
|
|
1923
1995
|
if (!value.hasOwnProperty(fieldKey)) {
|
|
1924
1996
|
return;
|
|
@@ -1930,14 +2002,14 @@ function createFormControl(props = {}) {
|
|
|
1930
2002
|
isObject(fieldValue) ||
|
|
1931
2003
|
(field && !field._f)) &&
|
|
1932
2004
|
!isDateObject(fieldValue)
|
|
1933
|
-
? setFieldValues(fieldName, fieldValue, options)
|
|
1934
|
-
: setFieldValue(fieldName, fieldValue, options);
|
|
2005
|
+
? setFieldValues(fieldName, fieldValue, options, skipClone, skipRender)
|
|
2006
|
+
: setFieldValue(fieldName, fieldValue, options, skipClone, skipRender);
|
|
1935
2007
|
}
|
|
1936
2008
|
};
|
|
1937
|
-
const
|
|
2009
|
+
const _setValue = (name, value, options, skipClone, skipStateEmit = false) => {
|
|
1938
2010
|
const field = get(_fields, name);
|
|
1939
2011
|
const isFieldArray = _names.array.has(name);
|
|
1940
|
-
const cloneValue = cloneObject(value);
|
|
2012
|
+
const cloneValue = skipClone ? value : cloneObject(value);
|
|
1941
2013
|
const previousValue = get(_formValues, name);
|
|
1942
2014
|
const isValueUnchanged = deepEqual(previousValue, cloneValue);
|
|
1943
2015
|
if (!isValueUnchanged) {
|
|
@@ -1946,7 +2018,7 @@ function createFormControl(props = {}) {
|
|
|
1946
2018
|
if (isFieldArray) {
|
|
1947
2019
|
_subjects.array.next({
|
|
1948
2020
|
name,
|
|
1949
|
-
values: cloneObject(_formValues),
|
|
2021
|
+
values: skipClone ? _formValues : cloneObject(_formValues),
|
|
1950
2022
|
});
|
|
1951
2023
|
if ((_proxyFormState.isDirty ||
|
|
1952
2024
|
_proxyFormState.dirtyFields ||
|
|
@@ -1954,31 +2026,28 @@ function createFormControl(props = {}) {
|
|
|
1954
2026
|
_proxySubscribeFormState.dirtyFields) &&
|
|
1955
2027
|
options.shouldDirty) {
|
|
1956
2028
|
_updateDirtyFields();
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
2029
|
+
if (!skipStateEmit) {
|
|
2030
|
+
_subjects.state.next({
|
|
2031
|
+
name,
|
|
2032
|
+
dirtyFields: _formState.dirtyFields,
|
|
2033
|
+
isDirty: _getDirty(name, cloneValue),
|
|
2034
|
+
});
|
|
2035
|
+
}
|
|
1962
2036
|
}
|
|
1963
2037
|
}
|
|
1964
2038
|
else {
|
|
1965
2039
|
const isEmpty = (Array.isArray(cloneValue) && !cloneValue.length) ||
|
|
1966
2040
|
isEmptyObject(cloneValue);
|
|
1967
2041
|
if (!field || field._f || isNullOrUndefined(cloneValue) || isEmpty) {
|
|
1968
|
-
setFieldValue(name, cloneValue, options);
|
|
2042
|
+
setFieldValue(name, cloneValue, options, skipClone, skipStateEmit);
|
|
1969
2043
|
}
|
|
1970
2044
|
else {
|
|
1971
|
-
setFieldValues(name, cloneValue, options);
|
|
2045
|
+
setFieldValues(name, cloneValue, options, skipClone, skipStateEmit);
|
|
1972
2046
|
}
|
|
1973
2047
|
}
|
|
1974
|
-
if (!isValueUnchanged) {
|
|
2048
|
+
if (!isValueUnchanged && !skipStateEmit) {
|
|
1975
2049
|
const watched = isWatched(name, _names);
|
|
1976
|
-
const values = cloneObject(_formValues);
|
|
1977
|
-
if (!isFieldArray) {
|
|
1978
|
-
for (const arrayName of getFieldArrayParentNames(_names.array, name)) {
|
|
1979
|
-
_subjects.array.next({ name: arrayName, values });
|
|
1980
|
-
}
|
|
1981
|
-
}
|
|
2050
|
+
const values = skipClone ? _formValues : cloneObject(_formValues);
|
|
1982
2051
|
_subjects.state.next({
|
|
1983
2052
|
...(watched && _formState),
|
|
1984
2053
|
name: _state.mount || watched ? name : undefined,
|
|
@@ -1986,7 +2055,8 @@ function createFormControl(props = {}) {
|
|
|
1986
2055
|
});
|
|
1987
2056
|
}
|
|
1988
2057
|
};
|
|
1989
|
-
const
|
|
2058
|
+
const setValue = (name, value, options = {}) => _setValue(name, value, options, false);
|
|
2059
|
+
const setValues = (formValues, options = {}) => {
|
|
1990
2060
|
const updatedFormValues = isFunction(formValues)
|
|
1991
2061
|
? formValues(_formValues)
|
|
1992
2062
|
: formValues;
|
|
@@ -1995,10 +2065,21 @@ function createFormControl(props = {}) {
|
|
|
1995
2065
|
..._formValues,
|
|
1996
2066
|
...updatedFormValues,
|
|
1997
2067
|
};
|
|
2068
|
+
const flattenedUpdates = flatten(updatedFormValues);
|
|
1998
2069
|
for (const fieldName of _names.mount) {
|
|
1999
|
-
|
|
2070
|
+
if (fieldName in flattenedUpdates) {
|
|
2071
|
+
_setValue(fieldName, flattenedUpdates[fieldName], options, true, true);
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
_subjects.state.next({
|
|
2075
|
+
..._formState,
|
|
2076
|
+
name: undefined,
|
|
2077
|
+
type: undefined,
|
|
2078
|
+
...(_valuesSubscriberCount ? { values: _formValues } : {}),
|
|
2079
|
+
});
|
|
2080
|
+
if (options.shouldValidate) {
|
|
2081
|
+
_setValid();
|
|
2000
2082
|
}
|
|
2001
|
-
_subjects.state.next({ ..._formState, values: _formValues });
|
|
2002
2083
|
}
|
|
2003
2084
|
};
|
|
2004
2085
|
const onChange = async (event) => {
|
|
@@ -2013,8 +2094,6 @@ function createFormControl(props = {}) {
|
|
|
2013
2094
|
(isDateObject(fieldValue) && isNaN(fieldValue.getTime())) ||
|
|
2014
2095
|
deepEqual(fieldValue, get(_formValues, name, fieldValue));
|
|
2015
2096
|
};
|
|
2016
|
-
const validationModeBeforeSubmit = getValidationModes(_options.mode);
|
|
2017
|
-
const validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
|
|
2018
2097
|
if (field) {
|
|
2019
2098
|
let error;
|
|
2020
2099
|
let isValid;
|
|
@@ -2022,12 +2101,13 @@ function createFormControl(props = {}) {
|
|
|
2022
2101
|
? getFieldValue(field._f)
|
|
2023
2102
|
: getEventValue(event);
|
|
2024
2103
|
const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
|
|
2025
|
-
const
|
|
2104
|
+
const hasNoValidationEffect = !hasValidation(field._f) &&
|
|
2026
2105
|
!props.validate &&
|
|
2027
2106
|
!_options.resolver &&
|
|
2028
2107
|
!get(_formState.errors, name) &&
|
|
2029
|
-
!field._f.deps
|
|
2030
|
-
|
|
2108
|
+
!field._f.deps;
|
|
2109
|
+
const shouldSkipValidation = hasNoValidationEffect ||
|
|
2110
|
+
skipValidation(isBlurEvent, get(_formState.touchedFields, name), _formState.isSubmitted, _validationModeAfterSubmit, _validationModeBeforeSubmit);
|
|
2031
2111
|
const watched = isWatched(name, _names, isBlurEvent);
|
|
2032
2112
|
set(_formValues, name, fieldValue);
|
|
2033
2113
|
if (isBlurEvent) {
|
|
@@ -2045,10 +2125,13 @@ function createFormControl(props = {}) {
|
|
|
2045
2125
|
_subjects.state.next({
|
|
2046
2126
|
name,
|
|
2047
2127
|
type: event.type,
|
|
2048
|
-
|
|
2128
|
+
...(_valuesSubscriberCount
|
|
2129
|
+
? { values: cloneObject(_formValues) }
|
|
2130
|
+
: {}),
|
|
2049
2131
|
});
|
|
2050
2132
|
if (shouldSkipValidation) {
|
|
2051
|
-
if (
|
|
2133
|
+
if ((!hasNoValidationEffect || !_formState.isValid) &&
|
|
2134
|
+
(_proxyFormState.isValid || _proxySubscribeFormState.isValid)) {
|
|
2052
2135
|
if (_options.mode === 'onBlur') {
|
|
2053
2136
|
if (isBlurEvent) {
|
|
2054
2137
|
_setValid();
|
|
@@ -2072,13 +2155,15 @@ function createFormControl(props = {}) {
|
|
|
2072
2155
|
const { errors } = await _runSchema([name]);
|
|
2073
2156
|
_updateIsValidating([name]);
|
|
2074
2157
|
_updateIsFieldValueUpdated(fieldValue);
|
|
2075
|
-
if (isFieldValueUpdated) {
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
error = errorLookupResult.error;
|
|
2079
|
-
name = errorLookupResult.name;
|
|
2080
|
-
isValid = isEmptyObject(errors);
|
|
2158
|
+
if (!isFieldValueUpdated) {
|
|
2159
|
+
!isEmptyObject(fieldState) && _subjects.state.next(fieldState);
|
|
2160
|
+
return;
|
|
2081
2161
|
}
|
|
2162
|
+
const previousErrorLookupResult = schemaErrorLookup(_formState.errors, _fields, name);
|
|
2163
|
+
const errorLookupResult = schemaErrorLookup(errors, _fields, previousErrorLookupResult.name || name);
|
|
2164
|
+
error = errorLookupResult.error;
|
|
2165
|
+
name = errorLookupResult.name;
|
|
2166
|
+
isValid = isEmptyObject(errors);
|
|
2082
2167
|
}
|
|
2083
2168
|
else {
|
|
2084
2169
|
_updateIsValidating([name], true);
|
|
@@ -2181,8 +2266,6 @@ function createFormControl(props = {}) {
|
|
|
2181
2266
|
const names = name ? convertToArrayPayload(name) : undefined;
|
|
2182
2267
|
names?.forEach((inputName) => unset(_formState.errors, inputName));
|
|
2183
2268
|
if (names) {
|
|
2184
|
-
// Emit for each cleared field with the field name so that
|
|
2185
|
-
// shouldSubscribeByName can filter and avoid broad re-renders
|
|
2186
2269
|
names.forEach((inputName) => {
|
|
2187
2270
|
_subjects.state.next({
|
|
2188
2271
|
name: inputName,
|
|
@@ -2191,7 +2274,6 @@ function createFormControl(props = {}) {
|
|
|
2191
2274
|
});
|
|
2192
2275
|
}
|
|
2193
2276
|
else {
|
|
2194
|
-
// Clear all errors - emit without name to notify all subscribers
|
|
2195
2277
|
_subjects.state.next({
|
|
2196
2278
|
errors: {},
|
|
2197
2279
|
});
|
|
@@ -2200,7 +2282,6 @@ function createFormControl(props = {}) {
|
|
|
2200
2282
|
const setError = (name, error, options) => {
|
|
2201
2283
|
const ref = (get(_fields, name, { _f: {} })._f || {}).ref;
|
|
2202
2284
|
const currentError = get(_formState.errors, name) || {};
|
|
2203
|
-
// Don't override existing error messages elsewhere in the object tree.
|
|
2204
2285
|
const { ref: currentRef, message, type, ...restOfErrorTree } = currentError;
|
|
2205
2286
|
set(_formState.errors, name, {
|
|
2206
2287
|
...restOfErrorTree,
|
|
@@ -2214,21 +2295,59 @@ function createFormControl(props = {}) {
|
|
|
2214
2295
|
});
|
|
2215
2296
|
options && options.shouldFocus && ref && ref.focus && ref.focus();
|
|
2216
2297
|
};
|
|
2217
|
-
const watch = (name, defaultValue) =>
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2298
|
+
const watch = (name, defaultValue) => {
|
|
2299
|
+
if (isFunction(name)) {
|
|
2300
|
+
_valuesSubscriberCount++;
|
|
2301
|
+
const { unsubscribe } = _subjects.state.subscribe({
|
|
2302
|
+
next: (payload) => 'values' in payload &&
|
|
2303
|
+
name(payload.values || _getWatch(undefined, defaultValue), payload),
|
|
2304
|
+
});
|
|
2305
|
+
let called = false;
|
|
2306
|
+
return {
|
|
2307
|
+
unsubscribe: () => {
|
|
2308
|
+
if (called) {
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
called = true;
|
|
2312
|
+
_valuesSubscriberCount--;
|
|
2313
|
+
unsubscribe();
|
|
2314
|
+
},
|
|
2315
|
+
};
|
|
2316
|
+
}
|
|
2317
|
+
return _getWatch(name, defaultValue, true);
|
|
2318
|
+
};
|
|
2319
|
+
const _subscribe = (props) => {
|
|
2320
|
+
const needsValues = !!props.formState?.values;
|
|
2321
|
+
if (needsValues) {
|
|
2322
|
+
_valuesSubscriberCount++;
|
|
2323
|
+
}
|
|
2324
|
+
const { unsubscribe } = _subjects.state.subscribe({
|
|
2325
|
+
next: (formState) => {
|
|
2326
|
+
if (shouldSubscribeByName(props.name, formState.name, props.exact) &&
|
|
2327
|
+
shouldRenderFormState(formState, props.formState || _proxyFormState, _setFormState, props.reRenderRoot)) {
|
|
2328
|
+
const snapshot = { ..._formValues };
|
|
2329
|
+
props.callback({
|
|
2330
|
+
values: snapshot,
|
|
2331
|
+
..._formState,
|
|
2332
|
+
...formState,
|
|
2333
|
+
defaultValues: _defaultValues,
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
2336
|
+
},
|
|
2337
|
+
});
|
|
2338
|
+
if (!needsValues) {
|
|
2339
|
+
return unsubscribe;
|
|
2340
|
+
}
|
|
2341
|
+
let called = false;
|
|
2342
|
+
return () => {
|
|
2343
|
+
if (called) {
|
|
2344
|
+
return;
|
|
2229
2345
|
}
|
|
2230
|
-
|
|
2231
|
-
|
|
2346
|
+
called = true;
|
|
2347
|
+
_valuesSubscriberCount--;
|
|
2348
|
+
unsubscribe();
|
|
2349
|
+
};
|
|
2350
|
+
};
|
|
2232
2351
|
const subscribe = (props) => {
|
|
2233
2352
|
_state.mount = true;
|
|
2234
2353
|
_proxySubscribeFormState = {
|
|
@@ -2363,8 +2482,7 @@ function createFormControl(props = {}) {
|
|
|
2363
2482
|
field._f.mount = false;
|
|
2364
2483
|
}
|
|
2365
2484
|
(_options.shouldUnregister || options.shouldUnregister) &&
|
|
2366
|
-
!(
|
|
2367
|
-
_state.action) &&
|
|
2485
|
+
!(isNameInFieldArray(_names.array, name) && _state.action) &&
|
|
2368
2486
|
_names.unMount.add(name);
|
|
2369
2487
|
}
|
|
2370
2488
|
},
|
|
@@ -2476,7 +2594,7 @@ function createFormControl(props = {}) {
|
|
|
2476
2594
|
const updatedValues = formValues ? cloneObject(formValues) : _defaultValues;
|
|
2477
2595
|
const cloneUpdatedValues = cloneObject(updatedValues);
|
|
2478
2596
|
const isEmptyResetValues = isEmptyObject(formValues);
|
|
2479
|
-
const values =
|
|
2597
|
+
const values = cloneUpdatedValues;
|
|
2480
2598
|
if (!keepStateOptions.keepDefaultValues) {
|
|
2481
2599
|
_defaultValues = updatedValues;
|
|
2482
2600
|
}
|
|
@@ -2525,15 +2643,25 @@ function createFormControl(props = {}) {
|
|
|
2525
2643
|
_fields = {};
|
|
2526
2644
|
}
|
|
2527
2645
|
}
|
|
2528
|
-
|
|
2529
|
-
|
|
2646
|
+
if (_options.shouldUnregister) {
|
|
2647
|
+
_formValues = keepStateOptions.keepDefaultValues
|
|
2530
2648
|
? cloneObject(_defaultValues)
|
|
2531
|
-
: {}
|
|
2532
|
-
|
|
2649
|
+
: {};
|
|
2650
|
+
if (keepStateOptions.keepFieldsRef) {
|
|
2651
|
+
for (const fieldName of _names.mount) {
|
|
2652
|
+
set(_formValues, fieldName, get(values, fieldName));
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
else {
|
|
2657
|
+
_formValues = cloneObject(values);
|
|
2658
|
+
}
|
|
2533
2659
|
_subjects.array.next({
|
|
2534
2660
|
values: { ...values },
|
|
2535
2661
|
});
|
|
2536
2662
|
_subjects.state.next({
|
|
2663
|
+
name: undefined,
|
|
2664
|
+
type: undefined,
|
|
2537
2665
|
values: { ...values },
|
|
2538
2666
|
});
|
|
2539
2667
|
}
|
|
@@ -2555,9 +2683,6 @@ function createFormControl(props = {}) {
|
|
|
2555
2683
|
_state.watch = !!_options.shouldUnregister;
|
|
2556
2684
|
_state.keepIsValid = !!keepStateOptions.keepIsValid;
|
|
2557
2685
|
_state.action = false;
|
|
2558
|
-
// Clear errors synchronously to prevent validation errors on subsequent submissions
|
|
2559
|
-
// This fixes the issue where form.reset() causes validation errors on subsequent
|
|
2560
|
-
// submissions in Next.js 16 with Server Actions
|
|
2561
2686
|
if (!keepStateOptions.keepErrors) {
|
|
2562
2687
|
_formState.errors = {};
|
|
2563
2688
|
}
|
|
@@ -2569,8 +2694,10 @@ function createFormControl(props = {}) {
|
|
|
2569
2694
|
? false
|
|
2570
2695
|
: keepStateOptions.keepDirty
|
|
2571
2696
|
? _formState.isDirty
|
|
2572
|
-
:
|
|
2573
|
-
|
|
2697
|
+
: keepStateOptions.keepValues
|
|
2698
|
+
? _getDirty()
|
|
2699
|
+
: !!(keepStateOptions.keepDefaultValues &&
|
|
2700
|
+
!deepEqual(formValues, _defaultValues)),
|
|
2574
2701
|
isSubmitted: keepStateOptions.keepIsSubmitted
|
|
2575
2702
|
? _formState.isSubmitted
|
|
2576
2703
|
: false,
|
|
@@ -2607,8 +2734,6 @@ function createFormControl(props = {}) {
|
|
|
2607
2734
|
? fieldReference.refs[0]
|
|
2608
2735
|
: fieldReference.ref;
|
|
2609
2736
|
if (fieldRef.focus) {
|
|
2610
|
-
// Use setTimeout to ensure focus happens after any pending state updates
|
|
2611
|
-
// This fixes the issue where setFocus doesn't work immediately after setError
|
|
2612
2737
|
setTimeout(() => {
|
|
2613
2738
|
fieldRef.focus();
|
|
2614
2739
|
options.shouldSelect &&
|
|
@@ -2619,9 +2744,15 @@ function createFormControl(props = {}) {
|
|
|
2619
2744
|
}
|
|
2620
2745
|
};
|
|
2621
2746
|
const _setFormState = (updatedFormState) => {
|
|
2747
|
+
// `name`, `type`, and `values` describe the event that produced this
|
|
2748
|
+
// update, not the form's persisted state (they aren't part of
|
|
2749
|
+
// `FormState`). Merging them in would leak a stale `name`/`type` from
|
|
2750
|
+
// one event into a later, unrelated notification that doesn't specify
|
|
2751
|
+
// its own.
|
|
2752
|
+
const { name, type, values, ...formState } = updatedFormState;
|
|
2622
2753
|
_formState = {
|
|
2623
2754
|
..._formState,
|
|
2624
|
-
...
|
|
2755
|
+
...formState,
|
|
2625
2756
|
};
|
|
2626
2757
|
};
|
|
2627
2758
|
const _resetDefaultValues = () => isFunction(_options.defaultValues) &&
|
|
@@ -2631,6 +2762,21 @@ function createFormControl(props = {}) {
|
|
|
2631
2762
|
isLoading: false,
|
|
2632
2763
|
});
|
|
2633
2764
|
});
|
|
2765
|
+
const resetDefaultValues = (values, options = {}) => {
|
|
2766
|
+
_defaultValues = cloneObject(values);
|
|
2767
|
+
if (!options.keepDirty) {
|
|
2768
|
+
const newDirtyFields = getDirtyFields(_defaultValues, _formValues);
|
|
2769
|
+
_formState.dirtyFields = newDirtyFields;
|
|
2770
|
+
_formState.isDirty = !isEmptyObject(newDirtyFields);
|
|
2771
|
+
}
|
|
2772
|
+
if (!options.keepIsValid) {
|
|
2773
|
+
_setValid();
|
|
2774
|
+
}
|
|
2775
|
+
_subjects.state.next({
|
|
2776
|
+
..._formState,
|
|
2777
|
+
defaultValues: _defaultValues,
|
|
2778
|
+
});
|
|
2779
|
+
};
|
|
2634
2780
|
const methods = {
|
|
2635
2781
|
control: {
|
|
2636
2782
|
register,
|
|
@@ -2687,6 +2833,8 @@ function createFormControl(props = {}) {
|
|
|
2687
2833
|
..._options,
|
|
2688
2834
|
...value,
|
|
2689
2835
|
};
|
|
2836
|
+
_validationModeBeforeSubmit = getValidationModes(_options.mode);
|
|
2837
|
+
_validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
|
|
2690
2838
|
},
|
|
2691
2839
|
},
|
|
2692
2840
|
subscribe,
|
|
@@ -2699,6 +2847,7 @@ function createFormControl(props = {}) {
|
|
|
2699
2847
|
getValues,
|
|
2700
2848
|
reset,
|
|
2701
2849
|
resetField,
|
|
2850
|
+
resetDefaultValues,
|
|
2702
2851
|
clearErrors,
|
|
2703
2852
|
unregister,
|
|
2704
2853
|
setError,
|
|
@@ -2819,30 +2968,45 @@ var updateAt = (fieldValues, index, value) => {
|
|
|
2819
2968
|
*/
|
|
2820
2969
|
function useFieldArray(props) {
|
|
2821
2970
|
const formControl = useFormControlContext();
|
|
2822
|
-
const { control = formControl, name, shouldUnregister, rules } = props;
|
|
2971
|
+
const { control = formControl, name, shouldUnregister, rules, disabled, } = props;
|
|
2823
2972
|
const [fields, setFields] = React.useState(control._getFieldArray(name));
|
|
2824
2973
|
const ids = React.useRef(control._getFieldArray(name).map(generateId));
|
|
2825
2974
|
const _actioned = React.useRef(false);
|
|
2826
|
-
|
|
2827
|
-
|
|
2975
|
+
if (!disabled) {
|
|
2976
|
+
control._names.array.add(name);
|
|
2977
|
+
}
|
|
2978
|
+
React.useMemo(() => !disabled &&
|
|
2979
|
+
rules &&
|
|
2828
2980
|
fields.length >= 0 &&
|
|
2829
|
-
control.register(name, rules), [control, name, fields.length, rules]);
|
|
2830
|
-
useIsomorphicLayoutEffect(() =>
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2981
|
+
control.register(name, rules), [control, name, fields.length, rules, disabled]);
|
|
2982
|
+
useIsomorphicLayoutEffect(() => {
|
|
2983
|
+
if (disabled) {
|
|
2984
|
+
return;
|
|
2985
|
+
}
|
|
2986
|
+
return control._subjects.array.subscribe({
|
|
2987
|
+
next: ({ values, name: fieldArrayName, }) => {
|
|
2988
|
+
if (fieldArrayName === name || !fieldArrayName) {
|
|
2989
|
+
const fieldValues = get(values, name);
|
|
2990
|
+
if (Array.isArray(fieldValues)) {
|
|
2991
|
+
setFields(fieldValues);
|
|
2992
|
+
ids.current = fieldValues.map(generateId);
|
|
2993
|
+
}
|
|
2994
|
+
else if (!fieldArrayName) {
|
|
2995
|
+
setFields([]);
|
|
2996
|
+
ids.current = [];
|
|
2997
|
+
}
|
|
2837
2998
|
}
|
|
2838
|
-
}
|
|
2839
|
-
}
|
|
2840
|
-
}
|
|
2999
|
+
},
|
|
3000
|
+
}).unsubscribe;
|
|
3001
|
+
}, [control, name, disabled]);
|
|
2841
3002
|
const updateValues = React.useCallback((updatedFieldArrayValues) => {
|
|
2842
3003
|
_actioned.current = true;
|
|
2843
3004
|
control._setFieldArray(name, updatedFieldArrayValues);
|
|
2844
3005
|
}, [control, name]);
|
|
2845
3006
|
const append = (value, options) => {
|
|
3007
|
+
if (disabled) {
|
|
3008
|
+
return;
|
|
3009
|
+
}
|
|
2846
3010
|
const appendValue = convertToArrayPayload(cloneObject(value));
|
|
2847
3011
|
const updatedFieldArrayValues = appendAt(control._getFieldArray(name), appendValue);
|
|
2848
3012
|
control._names.focus = getFocusFieldName(name, updatedFieldArrayValues.length - 1, options);
|
|
@@ -2854,6 +3018,9 @@ function useFieldArray(props) {
|
|
|
2854
3018
|
});
|
|
2855
3019
|
};
|
|
2856
3020
|
const prepend = (value, options) => {
|
|
3021
|
+
if (disabled) {
|
|
3022
|
+
return;
|
|
3023
|
+
}
|
|
2857
3024
|
const prependValue = convertToArrayPayload(cloneObject(value));
|
|
2858
3025
|
const updatedFieldArrayValues = prependAt(control._getFieldArray(name), prependValue);
|
|
2859
3026
|
control._names.focus = getFocusFieldName(name, 0, options);
|
|
@@ -2865,6 +3032,9 @@ function useFieldArray(props) {
|
|
|
2865
3032
|
});
|
|
2866
3033
|
};
|
|
2867
3034
|
const remove = (index) => {
|
|
3035
|
+
if (disabled) {
|
|
3036
|
+
return;
|
|
3037
|
+
}
|
|
2868
3038
|
const updatedFieldArrayValues = removeArrayAt(control._getFieldArray(name), index);
|
|
2869
3039
|
ids.current = removeArrayAt(ids.current, index);
|
|
2870
3040
|
updateValues(updatedFieldArrayValues);
|
|
@@ -2876,6 +3046,9 @@ function useFieldArray(props) {
|
|
|
2876
3046
|
});
|
|
2877
3047
|
};
|
|
2878
3048
|
const insert$1 = (index, value, options) => {
|
|
3049
|
+
if (disabled) {
|
|
3050
|
+
return;
|
|
3051
|
+
}
|
|
2879
3052
|
const insertValue = convertToArrayPayload(cloneObject(value));
|
|
2880
3053
|
const updatedFieldArrayValues = insert(control._getFieldArray(name), index, insertValue);
|
|
2881
3054
|
control._names.focus = getFocusFieldName(name, index, options);
|
|
@@ -2888,6 +3061,9 @@ function useFieldArray(props) {
|
|
|
2888
3061
|
});
|
|
2889
3062
|
};
|
|
2890
3063
|
const swap = (indexA, indexB) => {
|
|
3064
|
+
if (disabled) {
|
|
3065
|
+
return;
|
|
3066
|
+
}
|
|
2891
3067
|
const updatedFieldArrayValues = control._getFieldArray(name);
|
|
2892
3068
|
swapArrayAt(updatedFieldArrayValues, indexA, indexB);
|
|
2893
3069
|
swapArrayAt(ids.current, indexA, indexB);
|
|
@@ -2899,6 +3075,9 @@ function useFieldArray(props) {
|
|
|
2899
3075
|
}, false);
|
|
2900
3076
|
};
|
|
2901
3077
|
const move = (from, to) => {
|
|
3078
|
+
if (disabled) {
|
|
3079
|
+
return;
|
|
3080
|
+
}
|
|
2902
3081
|
const updatedFieldArrayValues = control._getFieldArray(name);
|
|
2903
3082
|
moveArrayAt(updatedFieldArrayValues, from, to);
|
|
2904
3083
|
moveArrayAt(ids.current, from, to);
|
|
@@ -2910,6 +3089,9 @@ function useFieldArray(props) {
|
|
|
2910
3089
|
}, false);
|
|
2911
3090
|
};
|
|
2912
3091
|
const update = (index, value) => {
|
|
3092
|
+
if (disabled) {
|
|
3093
|
+
return;
|
|
3094
|
+
}
|
|
2913
3095
|
const updateValue = cloneObject(value);
|
|
2914
3096
|
const updatedFieldArrayValues = updateAt(control._getFieldArray(name), index, updateValue);
|
|
2915
3097
|
ids.current = [...updatedFieldArrayValues].map((item, i) => !item || i === index ? generateId() : ids.current[i]);
|
|
@@ -2921,6 +3103,9 @@ function useFieldArray(props) {
|
|
|
2921
3103
|
}, true, false);
|
|
2922
3104
|
};
|
|
2923
3105
|
const replace = (value) => {
|
|
3106
|
+
if (disabled) {
|
|
3107
|
+
return;
|
|
3108
|
+
}
|
|
2924
3109
|
const updatedFieldArrayValues = convertToArrayPayload(cloneObject(value));
|
|
2925
3110
|
ids.current = updatedFieldArrayValues.map(generateId);
|
|
2926
3111
|
updateValues([...updatedFieldArrayValues]);
|
|
@@ -2928,6 +3113,9 @@ function useFieldArray(props) {
|
|
|
2928
3113
|
control._setFieldArray(name, [...updatedFieldArrayValues], (data) => data, {}, true, false);
|
|
2929
3114
|
};
|
|
2930
3115
|
React.useEffect(() => {
|
|
3116
|
+
if (disabled) {
|
|
3117
|
+
return;
|
|
3118
|
+
}
|
|
2931
3119
|
control._state.action = false;
|
|
2932
3120
|
isWatched(name, control._names) &&
|
|
2933
3121
|
control._subjects.state.next({
|
|
@@ -2943,15 +3131,24 @@ function useFieldArray(props) {
|
|
|
2943
3131
|
control._updateIsValidating([name]);
|
|
2944
3132
|
const error = get(result.errors, name);
|
|
2945
3133
|
const existingError = get(control._formState.errors, name);
|
|
3134
|
+
const existingErrorType = existingError && (existingError.type || existingError.root?.type);
|
|
3135
|
+
const existingErrorMessage = existingError &&
|
|
3136
|
+
(existingError.message || existingError.root?.message);
|
|
2946
3137
|
if (existingError
|
|
2947
|
-
? (!error &&
|
|
3138
|
+
? (!error && existingErrorType) ||
|
|
2948
3139
|
(error &&
|
|
2949
|
-
(
|
|
2950
|
-
|
|
3140
|
+
(existingErrorType !== error.type ||
|
|
3141
|
+
existingErrorMessage !== error.message))
|
|
2951
3142
|
: error && error.type) {
|
|
2952
|
-
error
|
|
2953
|
-
|
|
2954
|
-
|
|
3143
|
+
if (error) {
|
|
3144
|
+
isObject(error) &&
|
|
3145
|
+
!Object.keys(error).some((key) => !Number.isNaN(+key))
|
|
3146
|
+
? updateFieldArrayRootError(control._formState.errors, { [name]: error }, name)
|
|
3147
|
+
: set(control._formState.errors, name, error);
|
|
3148
|
+
}
|
|
3149
|
+
else {
|
|
3150
|
+
unset(control._formState.errors, name);
|
|
3151
|
+
}
|
|
2955
3152
|
control._subjects.state.next({
|
|
2956
3153
|
errors: control._formState.errors,
|
|
2957
3154
|
});
|
|
@@ -2971,10 +3168,14 @@ function useFieldArray(props) {
|
|
|
2971
3168
|
}
|
|
2972
3169
|
}
|
|
2973
3170
|
}
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
3171
|
+
// External updates that change `fields` (e.g. reset() or setValue() on
|
|
3172
|
+
// the array) already notify subscribers with the up-to-date values
|
|
3173
|
+
// themselves, so only re-broadcast here for genuine array method calls.
|
|
3174
|
+
_actioned.current &&
|
|
3175
|
+
control._subjects.state.next({
|
|
3176
|
+
name,
|
|
3177
|
+
values: cloneObject(control._formValues),
|
|
3178
|
+
});
|
|
2978
3179
|
control._names.focus &&
|
|
2979
3180
|
iterateFieldsByAction(control._fields, (ref, key) => {
|
|
2980
3181
|
if (control._names.focus &&
|
|
@@ -2988,10 +3189,15 @@ function useFieldArray(props) {
|
|
|
2988
3189
|
control._names.focus = '';
|
|
2989
3190
|
control._setValid();
|
|
2990
3191
|
_actioned.current = false;
|
|
2991
|
-
}, [fields, name, control]);
|
|
3192
|
+
}, [fields, name, control, disabled]);
|
|
2992
3193
|
React.useEffect(() => {
|
|
2993
|
-
!
|
|
3194
|
+
if (!disabled) {
|
|
3195
|
+
!get(control._formValues, name) && control._setFieldArray(name);
|
|
3196
|
+
}
|
|
2994
3197
|
return () => {
|
|
3198
|
+
if (disabled) {
|
|
3199
|
+
return;
|
|
3200
|
+
}
|
|
2995
3201
|
const shouldKeepFieldArrayValues = !(control._options.shouldUnregister || shouldUnregister);
|
|
2996
3202
|
const updateMounted = (name, value) => {
|
|
2997
3203
|
const field = get(control._fields, name);
|
|
@@ -3009,20 +3215,31 @@ function useFieldArray(props) {
|
|
|
3009
3215
|
? updateMounted(name, false)
|
|
3010
3216
|
: control.unregister(name);
|
|
3011
3217
|
};
|
|
3012
|
-
}, [name, control, shouldUnregister]);
|
|
3218
|
+
}, [name, control, shouldUnregister, disabled]);
|
|
3013
3219
|
return {
|
|
3014
|
-
swap: React.useCallback(swap, [updateValues, name, control]),
|
|
3015
|
-
move: React.useCallback(move, [updateValues, name, control]),
|
|
3016
|
-
prepend: React.useCallback(prepend, [
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3220
|
+
swap: React.useCallback(swap, [updateValues, name, control, disabled]),
|
|
3221
|
+
move: React.useCallback(move, [updateValues, name, control, disabled]),
|
|
3222
|
+
prepend: React.useCallback(prepend, [
|
|
3223
|
+
updateValues,
|
|
3224
|
+
name,
|
|
3225
|
+
control,
|
|
3226
|
+
disabled,
|
|
3227
|
+
]),
|
|
3228
|
+
append: React.useCallback(append, [updateValues, name, control, disabled]),
|
|
3229
|
+
remove: React.useCallback(remove, [updateValues, name, control, disabled]),
|
|
3230
|
+
insert: React.useCallback(insert$1, [updateValues, name, control, disabled]),
|
|
3231
|
+
update: React.useCallback(update, [updateValues, name, control, disabled]),
|
|
3232
|
+
replace: React.useCallback(replace, [
|
|
3233
|
+
updateValues,
|
|
3234
|
+
name,
|
|
3235
|
+
control,
|
|
3236
|
+
disabled,
|
|
3237
|
+
]),
|
|
3022
3238
|
fields: React.useMemo(() => fields.map((field, index) => ({
|
|
3023
3239
|
...field,
|
|
3240
|
+
...(isBoolean(disabled) ? { disabled } : {}),
|
|
3024
3241
|
key: ids.current[index] || generateId(),
|
|
3025
|
-
})), [fields]),
|
|
3242
|
+
})), [fields, disabled]),
|
|
3026
3243
|
};
|
|
3027
3244
|
}
|
|
3028
3245
|
|
|
@@ -3069,6 +3286,7 @@ function updateMethodsReference(_formControl) {
|
|
|
3069
3286
|
function useForm(props = {}) {
|
|
3070
3287
|
const _formControl = React.useRef(undefined);
|
|
3071
3288
|
const _values = React.useRef(undefined);
|
|
3289
|
+
const _formControlProp = React.useRef(props.formControl);
|
|
3072
3290
|
const [formState, updateFormState] = React.useState(() => ({
|
|
3073
3291
|
...cloneObject(DEFAULT_FORM_STATE),
|
|
3074
3292
|
isLoading: isFunction(props.defaultValues),
|
|
@@ -3078,7 +3296,9 @@ function useForm(props = {}) {
|
|
|
3078
3296
|
? undefined
|
|
3079
3297
|
: props.defaultValues,
|
|
3080
3298
|
}));
|
|
3081
|
-
if (!_formControl.current
|
|
3299
|
+
if (!_formControl.current ||
|
|
3300
|
+
(props.formControl && _formControlProp.current !== props.formControl)) {
|
|
3301
|
+
_formControlProp.current = props.formControl;
|
|
3082
3302
|
if (props.formControl) {
|
|
3083
3303
|
_formControl.current = {
|
|
3084
3304
|
...props.formControl,
|