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
|
@@ -31,8 +31,8 @@ const INPUT_VALIDATION_RULES = {
|
|
|
31
31
|
required: 'required',
|
|
32
32
|
validate: 'validate',
|
|
33
33
|
};
|
|
34
|
-
const FORM_ERROR_TYPE = 'form';
|
|
35
34
|
const ROOT_ERROR_TYPE = 'root';
|
|
35
|
+
const PROTOTYPE_KEYWORDS = ['__proto__', 'constructor', 'prototype'];
|
|
36
36
|
|
|
37
37
|
var isDateObject = (value) => value instanceof Date;
|
|
38
38
|
|
|
@@ -108,7 +108,8 @@ var createSubject = () => {
|
|
|
108
108
|
|
|
109
109
|
var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
const isEmptyObjectWithCustomPrototype = (object, keys) => keys.length === 0 && !Array.isArray(object) && !isPlainObject(object);
|
|
112
|
+
function deepEqual(object1, object2, visited = new WeakMap()) {
|
|
112
113
|
if (object1 === object2) {
|
|
113
114
|
return true;
|
|
114
115
|
}
|
|
@@ -123,11 +124,25 @@ function deepEqual(object1, object2, visited = new WeakSet()) {
|
|
|
123
124
|
if (keys1.length !== keys2.length) {
|
|
124
125
|
return false;
|
|
125
126
|
}
|
|
126
|
-
if (
|
|
127
|
+
if (isEmptyObjectWithCustomPrototype(object1, keys1) ||
|
|
128
|
+
isEmptyObjectWithCustomPrototype(object2, keys2)) {
|
|
129
|
+
return Object.is(object1, object2);
|
|
130
|
+
}
|
|
131
|
+
if (!keys1.length && Array.isArray(object1) !== Array.isArray(object2)) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
const visitedPairs = visited.get(object1);
|
|
135
|
+
if (visitedPairs && visitedPairs.has(object2)) {
|
|
127
136
|
return true;
|
|
128
137
|
}
|
|
129
|
-
|
|
130
|
-
|
|
138
|
+
if (visitedPairs) {
|
|
139
|
+
visitedPairs.add(object2);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
const ws = new WeakSet();
|
|
143
|
+
ws.add(object2);
|
|
144
|
+
visited.set(object1, ws);
|
|
145
|
+
}
|
|
131
146
|
for (const key of keys1) {
|
|
132
147
|
const val1 = object1[key];
|
|
133
148
|
if (!(key in object2)) {
|
|
@@ -167,17 +182,40 @@ function extractFormValues(fieldsState, formValues) {
|
|
|
167
182
|
return values;
|
|
168
183
|
}
|
|
169
184
|
|
|
170
|
-
|
|
185
|
+
const flatten = (obj) => {
|
|
186
|
+
const output = {};
|
|
187
|
+
for (const key of Object.keys(obj)) {
|
|
188
|
+
if (isObjectType(obj[key]) &&
|
|
189
|
+
obj[key] !== null &&
|
|
190
|
+
!isDateObject(obj[key])) {
|
|
191
|
+
const nested = flatten(obj[key]);
|
|
192
|
+
for (const nestedKey of Object.keys(nested)) {
|
|
193
|
+
output[`${key}.${nestedKey}`] = nested[nestedKey];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
output[key] = obj[key];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return output;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const IS_KEY_RE = /^\w*$/;
|
|
204
|
+
var isKey = (value) => IS_KEY_RE.test(value);
|
|
171
205
|
|
|
172
206
|
var isUndefined = (val) => val === undefined;
|
|
173
207
|
|
|
174
|
-
|
|
208
|
+
const FIELD_PATH_RE = /[.[\]'"]/;
|
|
209
|
+
var stringToPath = (input) => input.split(FIELD_PATH_RE).filter(Boolean);
|
|
175
210
|
|
|
176
211
|
var get = (object, path, defaultValue) => {
|
|
177
212
|
if (!path || !isObject(object)) {
|
|
178
213
|
return defaultValue;
|
|
179
214
|
}
|
|
180
215
|
const paths = isKey(path) ? [path] : stringToPath(path);
|
|
216
|
+
if (paths.some((key) => PROTOTYPE_KEYWORDS.includes(key))) {
|
|
217
|
+
return defaultValue;
|
|
218
|
+
}
|
|
181
219
|
const result = paths.reduce((result, key) => {
|
|
182
220
|
return isNullOrUndefined(result) ? undefined : result[key];
|
|
183
221
|
}, object);
|
|
@@ -234,7 +272,7 @@ var set = (object, path, value) => {
|
|
|
234
272
|
? []
|
|
235
273
|
: {};
|
|
236
274
|
}
|
|
237
|
-
if (key
|
|
275
|
+
if (PROTOTYPE_KEYWORDS.includes(key)) {
|
|
238
276
|
return;
|
|
239
277
|
}
|
|
240
278
|
object[key] = newValue;
|
|
@@ -273,6 +311,9 @@ function unset(object, path) {
|
|
|
273
311
|
: isKey(path)
|
|
274
312
|
? [path]
|
|
275
313
|
: stringToPath(path);
|
|
314
|
+
if (paths.some((segment) => PROTOTYPE_KEYWORDS.includes(String(segment)))) {
|
|
315
|
+
return object;
|
|
316
|
+
}
|
|
276
317
|
const childObject = paths.length === 1 ? object : baseGet(object, paths);
|
|
277
318
|
const index = paths.length - 1;
|
|
278
319
|
const key = paths[index];
|
|
@@ -376,16 +417,6 @@ var getEventValue = (event) => isObject(event) && event.target
|
|
|
376
417
|
: event.target.value
|
|
377
418
|
: event;
|
|
378
419
|
|
|
379
|
-
var getFieldArrayParentNames = (names, name) => {
|
|
380
|
-
const parts = name.split('.');
|
|
381
|
-
const matches = [];
|
|
382
|
-
let prefix = parts[0];
|
|
383
|
-
for (let i = 1; i < parts.length; prefix += '.' + parts[i++]) {
|
|
384
|
-
!isNaN(+parts[i]) && names.has(prefix) && matches.push(prefix);
|
|
385
|
-
}
|
|
386
|
-
return matches;
|
|
387
|
-
};
|
|
388
|
-
|
|
389
420
|
const defaultResult = {
|
|
390
421
|
value: false,
|
|
391
422
|
isValid: false,
|
|
@@ -490,12 +521,22 @@ var getValidationModes = (mode) => ({
|
|
|
490
521
|
});
|
|
491
522
|
|
|
492
523
|
const ASYNC_FUNCTION = 'AsyncFunction';
|
|
493
|
-
var hasPromiseValidation = (fieldReference) =>
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
524
|
+
var hasPromiseValidation = (fieldReference) => {
|
|
525
|
+
if (!fieldReference || !fieldReference.validate)
|
|
526
|
+
return false;
|
|
527
|
+
if (isFunction(fieldReference.validate)) {
|
|
528
|
+
return fieldReference.validate.constructor.name === ASYNC_FUNCTION;
|
|
529
|
+
}
|
|
530
|
+
if (isObject(fieldReference.validate)) {
|
|
531
|
+
for (const key in fieldReference.validate) {
|
|
532
|
+
if (fieldReference.validate[key].constructor
|
|
533
|
+
.name === ASYNC_FUNCTION) {
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return false;
|
|
539
|
+
};
|
|
499
540
|
|
|
500
541
|
var hasValidation = (options) => options.mount &&
|
|
501
542
|
(options.required ||
|
|
@@ -506,11 +547,21 @@ var hasValidation = (options) => options.mount &&
|
|
|
506
547
|
options.pattern ||
|
|
507
548
|
options.validate);
|
|
508
549
|
|
|
509
|
-
var
|
|
510
|
-
(
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
550
|
+
var isNameInFieldArray = (names, name) => name
|
|
551
|
+
.split('.')
|
|
552
|
+
.some((part, index, arr) => !isNaN(Number(part)) && names.has(arr.slice(0, index).join('.')));
|
|
553
|
+
|
|
554
|
+
var isWatched = (name, _names, isBlurEvent) => {
|
|
555
|
+
if (isBlurEvent)
|
|
556
|
+
return false;
|
|
557
|
+
if (_names.watchAll || _names.watch.has(name))
|
|
558
|
+
return true;
|
|
559
|
+
for (const watchName of _names.watch) {
|
|
560
|
+
if (name.startsWith(watchName) && name.charAt(watchName.length) === '.')
|
|
561
|
+
return true;
|
|
562
|
+
}
|
|
563
|
+
return false;
|
|
564
|
+
};
|
|
514
565
|
|
|
515
566
|
const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
|
|
516
567
|
for (const key of fieldsNames || Object.keys(fields)) {
|
|
@@ -578,10 +629,10 @@ function schemaErrorLookup(errors, _fields, name) {
|
|
|
578
629
|
var shouldRenderFormState = (formStateData, _proxyFormState, updateFormState, isRoot) => {
|
|
579
630
|
updateFormState(formStateData);
|
|
580
631
|
const { name, ...formState } = formStateData;
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
632
|
+
const keys = Object.keys(formState);
|
|
633
|
+
return (!keys.length ||
|
|
634
|
+
(isRoot && keys.length >= Object.keys(_proxyFormState).length) ||
|
|
635
|
+
keys.find((key) => _proxyFormState[key] ===
|
|
585
636
|
(!isRoot || VALIDATION_MODE.all)));
|
|
586
637
|
};
|
|
587
638
|
|
|
@@ -590,7 +641,7 @@ var shouldSubscribeByName = (name, signalName, exact) => !name ||
|
|
|
590
641
|
name === signalName ||
|
|
591
642
|
convertToArrayPayload(name).some((currentName) => currentName &&
|
|
592
643
|
(exact
|
|
593
|
-
? currentName === signalName
|
|
644
|
+
? currentName === signalName || currentName.startsWith(signalName + '.')
|
|
594
645
|
: currentName.startsWith(signalName) ||
|
|
595
646
|
signalName.startsWith(currentName)));
|
|
596
647
|
|
|
@@ -613,7 +664,8 @@ var skipValidation = (isBlurEvent, isTouched, isSubmitted, reValidateMode, mode)
|
|
|
613
664
|
var unsetEmptyArray = (ref, name) => !compact(get(ref, name)).length && unset(ref, name);
|
|
614
665
|
|
|
615
666
|
var updateFieldArrayRootError = (errors, error, name) => {
|
|
616
|
-
const
|
|
667
|
+
const existingErrors = get(errors, name);
|
|
668
|
+
const fieldArrayErrors = Array.isArray(existingErrors) ? existingErrors : [];
|
|
617
669
|
set(fieldArrayErrors, ROOT_ERROR_TYPE, error[name]);
|
|
618
670
|
set(errors, name, fieldArrayErrors);
|
|
619
671
|
return errors;
|
|
@@ -647,7 +699,13 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
|
|
|
647
699
|
const inputRef = refs ? refs[0] : ref;
|
|
648
700
|
const setCustomValidity = (message) => {
|
|
649
701
|
if (shouldUseNativeValidation && inputRef.reportValidity) {
|
|
650
|
-
|
|
702
|
+
const validityMessage = isBoolean(message) ? '' : message || '';
|
|
703
|
+
if (refs) {
|
|
704
|
+
refs.forEach((ref) => ref.setCustomValidity(validityMessage));
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
inputRef.setCustomValidity(validityMessage);
|
|
708
|
+
}
|
|
651
709
|
inputRef.reportValidity();
|
|
652
710
|
}
|
|
653
711
|
};
|
|
@@ -660,8 +718,7 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
|
|
|
660
718
|
isUndefined(inputValue)) ||
|
|
661
719
|
(isHTMLElement(ref) && ref.value === '') ||
|
|
662
720
|
inputValue === '' ||
|
|
663
|
-
(Array.isArray(inputValue) && !inputValue.length)
|
|
664
|
-
(valueAsNumber && typeof inputValue === 'number' && isNaN(inputValue));
|
|
721
|
+
(Array.isArray(inputValue) && !inputValue.length);
|
|
665
722
|
const appendErrorsCurry = appendErrors.bind(null, name, validateAllFieldCriteria, error);
|
|
666
723
|
const getMinMaxMessage = (exceedMax, maxLengthMessage, minLengthMessage, maxType = INPUT_VALIDATION_RULES.maxLength, minType = INPUT_VALIDATION_RULES.minLength) => {
|
|
667
724
|
const message = exceedMax ? maxLengthMessage : minLengthMessage;
|
|
@@ -823,6 +880,7 @@ const defaultOptions = {
|
|
|
823
880
|
reValidateMode: VALIDATION_MODE.onChange,
|
|
824
881
|
shouldFocusError: true,
|
|
825
882
|
};
|
|
883
|
+
const FORM_ERROR_TYPE = 'form';
|
|
826
884
|
const DEFAULT_FORM_STATE = {
|
|
827
885
|
submitCount: 0,
|
|
828
886
|
isDirty: false,
|
|
@@ -870,6 +928,9 @@ function createFormControl(props = {}) {
|
|
|
870
928
|
};
|
|
871
929
|
let delayErrorCallback;
|
|
872
930
|
let timer = 0;
|
|
931
|
+
let _valuesSubscriberCount = 0;
|
|
932
|
+
let _validationModeBeforeSubmit = getValidationModes(_options.mode);
|
|
933
|
+
let _validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
|
|
873
934
|
const defaultProxyFormState = {
|
|
874
935
|
isDirty: false,
|
|
875
936
|
dirtyFields: false,
|
|
@@ -980,6 +1041,7 @@ function createFormControl(props = {}) {
|
|
|
980
1041
|
};
|
|
981
1042
|
const updateErrors = (name, error) => {
|
|
982
1043
|
set(_formState.errors, name, error);
|
|
1044
|
+
_formState.errors = { ..._formState.errors };
|
|
983
1045
|
_subjects.state.next({
|
|
984
1046
|
errors: _formState.errors,
|
|
985
1047
|
});
|
|
@@ -1022,12 +1084,6 @@ function createFormControl(props = {}) {
|
|
|
1022
1084
|
: setFieldValue(name, defaultValue);
|
|
1023
1085
|
if (_state.mount && !_state.action) {
|
|
1024
1086
|
_setValid();
|
|
1025
|
-
// Re-registering a field after a prior unregister puts its key back
|
|
1026
|
-
// into _formValues, which can flip isDirty back to false (#13397).
|
|
1027
|
-
// Only run when we are currently dirty, otherwise an initial register
|
|
1028
|
-
// for a field with no defaultValue would flip isDirty to true. Reset
|
|
1029
|
-
// paths repopulate _formValues before re-register, so the key is
|
|
1030
|
-
// present then and this branch is skipped (preserves keepDirty).
|
|
1031
1087
|
if (wasUnsetInFormValues &&
|
|
1032
1088
|
_formState.isDirty &&
|
|
1033
1089
|
(_proxyFormState.isDirty || _proxySubscribeFormState.isDirty)) {
|
|
@@ -1037,6 +1093,12 @@ function createFormControl(props = {}) {
|
|
|
1037
1093
|
_subjects.state.next({ ..._formState });
|
|
1038
1094
|
}
|
|
1039
1095
|
}
|
|
1096
|
+
if (props.shouldUnregister &&
|
|
1097
|
+
wasUnsetInFormValues &&
|
|
1098
|
+
!isUndefined(get(_formValues, name)) &&
|
|
1099
|
+
isWatched(name, _names)) {
|
|
1100
|
+
_state.watch = true;
|
|
1101
|
+
}
|
|
1040
1102
|
}
|
|
1041
1103
|
}
|
|
1042
1104
|
};
|
|
@@ -1048,12 +1110,13 @@ function createFormControl(props = {}) {
|
|
|
1048
1110
|
};
|
|
1049
1111
|
if (!_options.disabled) {
|
|
1050
1112
|
if (!isBlurEvent || shouldDirty) {
|
|
1113
|
+
const isCurrentFieldPristine = deepEqual(get(_defaultValues, name), fieldValue);
|
|
1051
1114
|
if (_proxyFormState.isDirty || _proxySubscribeFormState.isDirty) {
|
|
1052
1115
|
isPreviousDirty = _formState.isDirty;
|
|
1053
|
-
_formState.isDirty = output.isDirty =
|
|
1116
|
+
_formState.isDirty = output.isDirty =
|
|
1117
|
+
!isCurrentFieldPristine || _getDirty();
|
|
1054
1118
|
shouldUpdateField = isPreviousDirty !== output.isDirty;
|
|
1055
1119
|
}
|
|
1056
|
-
const isCurrentFieldPristine = deepEqual(get(_defaultValues, name), fieldValue);
|
|
1057
1120
|
isPreviousDirty = !!get(_formState.dirtyFields, name);
|
|
1058
1121
|
if (isCurrentFieldPristine !== _formState.isDirty) {
|
|
1059
1122
|
_formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
|
|
@@ -1101,6 +1164,7 @@ function createFormControl(props = {}) {
|
|
|
1101
1164
|
error
|
|
1102
1165
|
? set(_formState.errors, name, error)
|
|
1103
1166
|
: unset(_formState.errors, name);
|
|
1167
|
+
_formState.errors = { ..._formState.errors };
|
|
1104
1168
|
}
|
|
1105
1169
|
if ((error ? !deepEqual(previousFieldError, error) : previousFieldError) ||
|
|
1106
1170
|
!isEmptyObject(fieldState) ||
|
|
@@ -1129,11 +1193,14 @@ function createFormControl(props = {}) {
|
|
|
1129
1193
|
for (const name of names) {
|
|
1130
1194
|
const error = get(errors, name);
|
|
1131
1195
|
error
|
|
1132
|
-
? _names.array.has(name) &&
|
|
1196
|
+
? _names.array.has(name) &&
|
|
1197
|
+
isObject(error) &&
|
|
1198
|
+
!Object.keys(error).some((key) => !Number.isNaN(Number(key)))
|
|
1133
1199
|
? updateFieldArrayRootError(_formState.errors, { [name]: error }, name)
|
|
1134
1200
|
: set(_formState.errors, name, error)
|
|
1135
1201
|
: unset(_formState.errors, name);
|
|
1136
1202
|
}
|
|
1203
|
+
_formState.errors = { ..._formState.errors };
|
|
1137
1204
|
}
|
|
1138
1205
|
else {
|
|
1139
1206
|
_formState.errors = errors;
|
|
@@ -1248,7 +1315,7 @@ function createFormControl(props = {}) {
|
|
|
1248
1315
|
};
|
|
1249
1316
|
const _getDirty = (name, data) => !_options.disabled &&
|
|
1250
1317
|
(name && data && set(_formValues, name, data),
|
|
1251
|
-
!deepEqual(
|
|
1318
|
+
!deepEqual(_state.mount ? _formValues : _defaultValues, _defaultValues));
|
|
1252
1319
|
const _getWatch = (names, defaultValue, isGlobal) => generateWatchOutput(names, _names, {
|
|
1253
1320
|
...(_state.mount
|
|
1254
1321
|
? _formValues
|
|
@@ -1259,7 +1326,7 @@ function createFormControl(props = {}) {
|
|
|
1259
1326
|
: defaultValue),
|
|
1260
1327
|
}, isGlobal, defaultValue);
|
|
1261
1328
|
const _getFieldArray = (name) => compact(get(_state.mount ? _formValues : _defaultValues, name, _options.shouldUnregister ? get(_defaultValues, name, []) : []));
|
|
1262
|
-
const setFieldValue = (name, value, options = {}) => {
|
|
1329
|
+
const setFieldValue = (name, value, options = {}, skipClone = false, skipRender = false) => {
|
|
1263
1330
|
const field = get(_fields, name);
|
|
1264
1331
|
let fieldValue = value;
|
|
1265
1332
|
if (field) {
|
|
@@ -1297,20 +1364,20 @@ function createFormControl(props = {}) {
|
|
|
1297
1364
|
}
|
|
1298
1365
|
else {
|
|
1299
1366
|
fieldReference.ref.value = fieldValue;
|
|
1300
|
-
if (!fieldReference.ref.type) {
|
|
1367
|
+
if (!fieldReference.ref.type && !skipRender) {
|
|
1301
1368
|
_subjects.state.next({
|
|
1302
1369
|
name,
|
|
1303
|
-
values: cloneObject(_formValues),
|
|
1370
|
+
values: skipClone ? _formValues : cloneObject(_formValues),
|
|
1304
1371
|
});
|
|
1305
1372
|
}
|
|
1306
1373
|
}
|
|
1307
1374
|
}
|
|
1308
1375
|
}
|
|
1309
1376
|
(options.shouldDirty || options.shouldTouch) &&
|
|
1310
|
-
updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty,
|
|
1377
|
+
updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty, !skipRender);
|
|
1311
1378
|
options.shouldValidate && trigger(name);
|
|
1312
1379
|
};
|
|
1313
|
-
const setFieldValues = (name, value, options) => {
|
|
1380
|
+
const setFieldValues = (name, value, options, skipClone = false, skipRender = false) => {
|
|
1314
1381
|
for (const fieldKey in value) {
|
|
1315
1382
|
if (!value.hasOwnProperty(fieldKey)) {
|
|
1316
1383
|
return;
|
|
@@ -1322,14 +1389,14 @@ function createFormControl(props = {}) {
|
|
|
1322
1389
|
isObject(fieldValue) ||
|
|
1323
1390
|
(field && !field._f)) &&
|
|
1324
1391
|
!isDateObject(fieldValue)
|
|
1325
|
-
? setFieldValues(fieldName, fieldValue, options)
|
|
1326
|
-
: setFieldValue(fieldName, fieldValue, options);
|
|
1392
|
+
? setFieldValues(fieldName, fieldValue, options, skipClone, skipRender)
|
|
1393
|
+
: setFieldValue(fieldName, fieldValue, options, skipClone, skipRender);
|
|
1327
1394
|
}
|
|
1328
1395
|
};
|
|
1329
|
-
const
|
|
1396
|
+
const _setValue = (name, value, options, skipClone, skipStateEmit = false) => {
|
|
1330
1397
|
const field = get(_fields, name);
|
|
1331
1398
|
const isFieldArray = _names.array.has(name);
|
|
1332
|
-
const cloneValue = cloneObject(value);
|
|
1399
|
+
const cloneValue = skipClone ? value : cloneObject(value);
|
|
1333
1400
|
const previousValue = get(_formValues, name);
|
|
1334
1401
|
const isValueUnchanged = deepEqual(previousValue, cloneValue);
|
|
1335
1402
|
if (!isValueUnchanged) {
|
|
@@ -1338,7 +1405,7 @@ function createFormControl(props = {}) {
|
|
|
1338
1405
|
if (isFieldArray) {
|
|
1339
1406
|
_subjects.array.next({
|
|
1340
1407
|
name,
|
|
1341
|
-
values: cloneObject(_formValues),
|
|
1408
|
+
values: skipClone ? _formValues : cloneObject(_formValues),
|
|
1342
1409
|
});
|
|
1343
1410
|
if ((_proxyFormState.isDirty ||
|
|
1344
1411
|
_proxyFormState.dirtyFields ||
|
|
@@ -1346,31 +1413,28 @@ function createFormControl(props = {}) {
|
|
|
1346
1413
|
_proxySubscribeFormState.dirtyFields) &&
|
|
1347
1414
|
options.shouldDirty) {
|
|
1348
1415
|
_updateDirtyFields();
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1416
|
+
if (!skipStateEmit) {
|
|
1417
|
+
_subjects.state.next({
|
|
1418
|
+
name,
|
|
1419
|
+
dirtyFields: _formState.dirtyFields,
|
|
1420
|
+
isDirty: _getDirty(name, cloneValue),
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1354
1423
|
}
|
|
1355
1424
|
}
|
|
1356
1425
|
else {
|
|
1357
1426
|
const isEmpty = (Array.isArray(cloneValue) && !cloneValue.length) ||
|
|
1358
1427
|
isEmptyObject(cloneValue);
|
|
1359
1428
|
if (!field || field._f || isNullOrUndefined(cloneValue) || isEmpty) {
|
|
1360
|
-
setFieldValue(name, cloneValue, options);
|
|
1429
|
+
setFieldValue(name, cloneValue, options, skipClone, skipStateEmit);
|
|
1361
1430
|
}
|
|
1362
1431
|
else {
|
|
1363
|
-
setFieldValues(name, cloneValue, options);
|
|
1432
|
+
setFieldValues(name, cloneValue, options, skipClone, skipStateEmit);
|
|
1364
1433
|
}
|
|
1365
1434
|
}
|
|
1366
|
-
if (!isValueUnchanged) {
|
|
1435
|
+
if (!isValueUnchanged && !skipStateEmit) {
|
|
1367
1436
|
const watched = isWatched(name, _names);
|
|
1368
|
-
const values = cloneObject(_formValues);
|
|
1369
|
-
if (!isFieldArray) {
|
|
1370
|
-
for (const arrayName of getFieldArrayParentNames(_names.array, name)) {
|
|
1371
|
-
_subjects.array.next({ name: arrayName, values });
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1437
|
+
const values = skipClone ? _formValues : cloneObject(_formValues);
|
|
1374
1438
|
_subjects.state.next({
|
|
1375
1439
|
...(watched && _formState),
|
|
1376
1440
|
name: _state.mount || watched ? name : undefined,
|
|
@@ -1378,7 +1442,8 @@ function createFormControl(props = {}) {
|
|
|
1378
1442
|
});
|
|
1379
1443
|
}
|
|
1380
1444
|
};
|
|
1381
|
-
const
|
|
1445
|
+
const setValue = (name, value, options = {}) => _setValue(name, value, options, false);
|
|
1446
|
+
const setValues = (formValues, options = {}) => {
|
|
1382
1447
|
const updatedFormValues = isFunction(formValues)
|
|
1383
1448
|
? formValues(_formValues)
|
|
1384
1449
|
: formValues;
|
|
@@ -1387,10 +1452,21 @@ function createFormControl(props = {}) {
|
|
|
1387
1452
|
..._formValues,
|
|
1388
1453
|
...updatedFormValues,
|
|
1389
1454
|
};
|
|
1455
|
+
const flattenedUpdates = flatten(updatedFormValues);
|
|
1390
1456
|
for (const fieldName of _names.mount) {
|
|
1391
|
-
|
|
1457
|
+
if (fieldName in flattenedUpdates) {
|
|
1458
|
+
_setValue(fieldName, flattenedUpdates[fieldName], options, true, true);
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
_subjects.state.next({
|
|
1462
|
+
..._formState,
|
|
1463
|
+
name: undefined,
|
|
1464
|
+
type: undefined,
|
|
1465
|
+
...(_valuesSubscriberCount ? { values: _formValues } : {}),
|
|
1466
|
+
});
|
|
1467
|
+
if (options.shouldValidate) {
|
|
1468
|
+
_setValid();
|
|
1392
1469
|
}
|
|
1393
|
-
_subjects.state.next({ ..._formState, values: _formValues });
|
|
1394
1470
|
}
|
|
1395
1471
|
};
|
|
1396
1472
|
const onChange = async (event) => {
|
|
@@ -1405,8 +1481,6 @@ function createFormControl(props = {}) {
|
|
|
1405
1481
|
(isDateObject(fieldValue) && isNaN(fieldValue.getTime())) ||
|
|
1406
1482
|
deepEqual(fieldValue, get(_formValues, name, fieldValue));
|
|
1407
1483
|
};
|
|
1408
|
-
const validationModeBeforeSubmit = getValidationModes(_options.mode);
|
|
1409
|
-
const validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
|
|
1410
1484
|
if (field) {
|
|
1411
1485
|
let error;
|
|
1412
1486
|
let isValid;
|
|
@@ -1414,12 +1488,13 @@ function createFormControl(props = {}) {
|
|
|
1414
1488
|
? getFieldValue(field._f)
|
|
1415
1489
|
: getEventValue(event);
|
|
1416
1490
|
const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
|
|
1417
|
-
const
|
|
1491
|
+
const hasNoValidationEffect = !hasValidation(field._f) &&
|
|
1418
1492
|
!props.validate &&
|
|
1419
1493
|
!_options.resolver &&
|
|
1420
1494
|
!get(_formState.errors, name) &&
|
|
1421
|
-
!field._f.deps
|
|
1422
|
-
|
|
1495
|
+
!field._f.deps;
|
|
1496
|
+
const shouldSkipValidation = hasNoValidationEffect ||
|
|
1497
|
+
skipValidation(isBlurEvent, get(_formState.touchedFields, name), _formState.isSubmitted, _validationModeAfterSubmit, _validationModeBeforeSubmit);
|
|
1423
1498
|
const watched = isWatched(name, _names, isBlurEvent);
|
|
1424
1499
|
set(_formValues, name, fieldValue);
|
|
1425
1500
|
if (isBlurEvent) {
|
|
@@ -1437,10 +1512,13 @@ function createFormControl(props = {}) {
|
|
|
1437
1512
|
_subjects.state.next({
|
|
1438
1513
|
name,
|
|
1439
1514
|
type: event.type,
|
|
1440
|
-
|
|
1515
|
+
...(_valuesSubscriberCount
|
|
1516
|
+
? { values: cloneObject(_formValues) }
|
|
1517
|
+
: {}),
|
|
1441
1518
|
});
|
|
1442
1519
|
if (shouldSkipValidation) {
|
|
1443
|
-
if (
|
|
1520
|
+
if ((!hasNoValidationEffect || !_formState.isValid) &&
|
|
1521
|
+
(_proxyFormState.isValid || _proxySubscribeFormState.isValid)) {
|
|
1444
1522
|
if (_options.mode === 'onBlur') {
|
|
1445
1523
|
if (isBlurEvent) {
|
|
1446
1524
|
_setValid();
|
|
@@ -1464,13 +1542,15 @@ function createFormControl(props = {}) {
|
|
|
1464
1542
|
const { errors } = await _runSchema([name]);
|
|
1465
1543
|
_updateIsValidating([name]);
|
|
1466
1544
|
_updateIsFieldValueUpdated(fieldValue);
|
|
1467
|
-
if (isFieldValueUpdated) {
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
error = errorLookupResult.error;
|
|
1471
|
-
name = errorLookupResult.name;
|
|
1472
|
-
isValid = isEmptyObject(errors);
|
|
1545
|
+
if (!isFieldValueUpdated) {
|
|
1546
|
+
!isEmptyObject(fieldState) && _subjects.state.next(fieldState);
|
|
1547
|
+
return;
|
|
1473
1548
|
}
|
|
1549
|
+
const previousErrorLookupResult = schemaErrorLookup(_formState.errors, _fields, name);
|
|
1550
|
+
const errorLookupResult = schemaErrorLookup(errors, _fields, previousErrorLookupResult.name || name);
|
|
1551
|
+
error = errorLookupResult.error;
|
|
1552
|
+
name = errorLookupResult.name;
|
|
1553
|
+
isValid = isEmptyObject(errors);
|
|
1474
1554
|
}
|
|
1475
1555
|
else {
|
|
1476
1556
|
_updateIsValidating([name], true);
|
|
@@ -1573,8 +1653,6 @@ function createFormControl(props = {}) {
|
|
|
1573
1653
|
const names = name ? convertToArrayPayload(name) : undefined;
|
|
1574
1654
|
names?.forEach((inputName) => unset(_formState.errors, inputName));
|
|
1575
1655
|
if (names) {
|
|
1576
|
-
// Emit for each cleared field with the field name so that
|
|
1577
|
-
// shouldSubscribeByName can filter and avoid broad re-renders
|
|
1578
1656
|
names.forEach((inputName) => {
|
|
1579
1657
|
_subjects.state.next({
|
|
1580
1658
|
name: inputName,
|
|
@@ -1583,7 +1661,6 @@ function createFormControl(props = {}) {
|
|
|
1583
1661
|
});
|
|
1584
1662
|
}
|
|
1585
1663
|
else {
|
|
1586
|
-
// Clear all errors - emit without name to notify all subscribers
|
|
1587
1664
|
_subjects.state.next({
|
|
1588
1665
|
errors: {},
|
|
1589
1666
|
});
|
|
@@ -1592,7 +1669,6 @@ function createFormControl(props = {}) {
|
|
|
1592
1669
|
const setError = (name, error, options) => {
|
|
1593
1670
|
const ref = (get(_fields, name, { _f: {} })._f || {}).ref;
|
|
1594
1671
|
const currentError = get(_formState.errors, name) || {};
|
|
1595
|
-
// Don't override existing error messages elsewhere in the object tree.
|
|
1596
1672
|
const { ref: currentRef, message, type, ...restOfErrorTree } = currentError;
|
|
1597
1673
|
set(_formState.errors, name, {
|
|
1598
1674
|
...restOfErrorTree,
|
|
@@ -1606,21 +1682,59 @@ function createFormControl(props = {}) {
|
|
|
1606
1682
|
});
|
|
1607
1683
|
options && options.shouldFocus && ref && ref.focus && ref.focus();
|
|
1608
1684
|
};
|
|
1609
|
-
const watch = (name, defaultValue) =>
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1685
|
+
const watch = (name, defaultValue) => {
|
|
1686
|
+
if (isFunction(name)) {
|
|
1687
|
+
_valuesSubscriberCount++;
|
|
1688
|
+
const { unsubscribe } = _subjects.state.subscribe({
|
|
1689
|
+
next: (payload) => 'values' in payload &&
|
|
1690
|
+
name(payload.values || _getWatch(undefined, defaultValue), payload),
|
|
1691
|
+
});
|
|
1692
|
+
let called = false;
|
|
1693
|
+
return {
|
|
1694
|
+
unsubscribe: () => {
|
|
1695
|
+
if (called) {
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
called = true;
|
|
1699
|
+
_valuesSubscriberCount--;
|
|
1700
|
+
unsubscribe();
|
|
1701
|
+
},
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
return _getWatch(name, defaultValue, true);
|
|
1705
|
+
};
|
|
1706
|
+
const _subscribe = (props) => {
|
|
1707
|
+
const needsValues = !!props.formState?.values;
|
|
1708
|
+
if (needsValues) {
|
|
1709
|
+
_valuesSubscriberCount++;
|
|
1710
|
+
}
|
|
1711
|
+
const { unsubscribe } = _subjects.state.subscribe({
|
|
1712
|
+
next: (formState) => {
|
|
1713
|
+
if (shouldSubscribeByName(props.name, formState.name, props.exact) &&
|
|
1714
|
+
shouldRenderFormState(formState, props.formState || _proxyFormState, _setFormState, props.reRenderRoot)) {
|
|
1715
|
+
const snapshot = { ..._formValues };
|
|
1716
|
+
props.callback({
|
|
1717
|
+
values: snapshot,
|
|
1718
|
+
..._formState,
|
|
1719
|
+
...formState,
|
|
1720
|
+
defaultValues: _defaultValues,
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
},
|
|
1724
|
+
});
|
|
1725
|
+
if (!needsValues) {
|
|
1726
|
+
return unsubscribe;
|
|
1727
|
+
}
|
|
1728
|
+
let called = false;
|
|
1729
|
+
return () => {
|
|
1730
|
+
if (called) {
|
|
1731
|
+
return;
|
|
1621
1732
|
}
|
|
1622
|
-
|
|
1623
|
-
|
|
1733
|
+
called = true;
|
|
1734
|
+
_valuesSubscriberCount--;
|
|
1735
|
+
unsubscribe();
|
|
1736
|
+
};
|
|
1737
|
+
};
|
|
1624
1738
|
const subscribe = (props) => {
|
|
1625
1739
|
_state.mount = true;
|
|
1626
1740
|
_proxySubscribeFormState = {
|
|
@@ -1755,8 +1869,7 @@ function createFormControl(props = {}) {
|
|
|
1755
1869
|
field._f.mount = false;
|
|
1756
1870
|
}
|
|
1757
1871
|
(_options.shouldUnregister || options.shouldUnregister) &&
|
|
1758
|
-
!(
|
|
1759
|
-
_state.action) &&
|
|
1872
|
+
!(isNameInFieldArray(_names.array, name) && _state.action) &&
|
|
1760
1873
|
_names.unMount.add(name);
|
|
1761
1874
|
}
|
|
1762
1875
|
},
|
|
@@ -1868,7 +1981,7 @@ function createFormControl(props = {}) {
|
|
|
1868
1981
|
const updatedValues = formValues ? cloneObject(formValues) : _defaultValues;
|
|
1869
1982
|
const cloneUpdatedValues = cloneObject(updatedValues);
|
|
1870
1983
|
const isEmptyResetValues = isEmptyObject(formValues);
|
|
1871
|
-
const values =
|
|
1984
|
+
const values = cloneUpdatedValues;
|
|
1872
1985
|
if (!keepStateOptions.keepDefaultValues) {
|
|
1873
1986
|
_defaultValues = updatedValues;
|
|
1874
1987
|
}
|
|
@@ -1917,15 +2030,25 @@ function createFormControl(props = {}) {
|
|
|
1917
2030
|
_fields = {};
|
|
1918
2031
|
}
|
|
1919
2032
|
}
|
|
1920
|
-
|
|
1921
|
-
|
|
2033
|
+
if (_options.shouldUnregister) {
|
|
2034
|
+
_formValues = keepStateOptions.keepDefaultValues
|
|
1922
2035
|
? cloneObject(_defaultValues)
|
|
1923
|
-
: {}
|
|
1924
|
-
|
|
2036
|
+
: {};
|
|
2037
|
+
if (keepStateOptions.keepFieldsRef) {
|
|
2038
|
+
for (const fieldName of _names.mount) {
|
|
2039
|
+
set(_formValues, fieldName, get(values, fieldName));
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
else {
|
|
2044
|
+
_formValues = cloneObject(values);
|
|
2045
|
+
}
|
|
1925
2046
|
_subjects.array.next({
|
|
1926
2047
|
values: { ...values },
|
|
1927
2048
|
});
|
|
1928
2049
|
_subjects.state.next({
|
|
2050
|
+
name: undefined,
|
|
2051
|
+
type: undefined,
|
|
1929
2052
|
values: { ...values },
|
|
1930
2053
|
});
|
|
1931
2054
|
}
|
|
@@ -1947,9 +2070,6 @@ function createFormControl(props = {}) {
|
|
|
1947
2070
|
_state.watch = !!_options.shouldUnregister;
|
|
1948
2071
|
_state.keepIsValid = !!keepStateOptions.keepIsValid;
|
|
1949
2072
|
_state.action = false;
|
|
1950
|
-
// Clear errors synchronously to prevent validation errors on subsequent submissions
|
|
1951
|
-
// This fixes the issue where form.reset() causes validation errors on subsequent
|
|
1952
|
-
// submissions in Next.js 16 with Server Actions
|
|
1953
2073
|
if (!keepStateOptions.keepErrors) {
|
|
1954
2074
|
_formState.errors = {};
|
|
1955
2075
|
}
|
|
@@ -1961,8 +2081,10 @@ function createFormControl(props = {}) {
|
|
|
1961
2081
|
? false
|
|
1962
2082
|
: keepStateOptions.keepDirty
|
|
1963
2083
|
? _formState.isDirty
|
|
1964
|
-
:
|
|
1965
|
-
|
|
2084
|
+
: keepStateOptions.keepValues
|
|
2085
|
+
? _getDirty()
|
|
2086
|
+
: !!(keepStateOptions.keepDefaultValues &&
|
|
2087
|
+
!deepEqual(formValues, _defaultValues)),
|
|
1966
2088
|
isSubmitted: keepStateOptions.keepIsSubmitted
|
|
1967
2089
|
? _formState.isSubmitted
|
|
1968
2090
|
: false,
|
|
@@ -1999,8 +2121,6 @@ function createFormControl(props = {}) {
|
|
|
1999
2121
|
? fieldReference.refs[0]
|
|
2000
2122
|
: fieldReference.ref;
|
|
2001
2123
|
if (fieldRef.focus) {
|
|
2002
|
-
// Use setTimeout to ensure focus happens after any pending state updates
|
|
2003
|
-
// This fixes the issue where setFocus doesn't work immediately after setError
|
|
2004
2124
|
setTimeout(() => {
|
|
2005
2125
|
fieldRef.focus();
|
|
2006
2126
|
options.shouldSelect &&
|
|
@@ -2011,9 +2131,15 @@ function createFormControl(props = {}) {
|
|
|
2011
2131
|
}
|
|
2012
2132
|
};
|
|
2013
2133
|
const _setFormState = (updatedFormState) => {
|
|
2134
|
+
// `name`, `type`, and `values` describe the event that produced this
|
|
2135
|
+
// update, not the form's persisted state (they aren't part of
|
|
2136
|
+
// `FormState`). Merging them in would leak a stale `name`/`type` from
|
|
2137
|
+
// one event into a later, unrelated notification that doesn't specify
|
|
2138
|
+
// its own.
|
|
2139
|
+
const { name, type, values, ...formState } = updatedFormState;
|
|
2014
2140
|
_formState = {
|
|
2015
2141
|
..._formState,
|
|
2016
|
-
...
|
|
2142
|
+
...formState,
|
|
2017
2143
|
};
|
|
2018
2144
|
};
|
|
2019
2145
|
const _resetDefaultValues = () => isFunction(_options.defaultValues) &&
|
|
@@ -2023,6 +2149,21 @@ function createFormControl(props = {}) {
|
|
|
2023
2149
|
isLoading: false,
|
|
2024
2150
|
});
|
|
2025
2151
|
});
|
|
2152
|
+
const resetDefaultValues = (values, options = {}) => {
|
|
2153
|
+
_defaultValues = cloneObject(values);
|
|
2154
|
+
if (!options.keepDirty) {
|
|
2155
|
+
const newDirtyFields = getDirtyFields(_defaultValues, _formValues);
|
|
2156
|
+
_formState.dirtyFields = newDirtyFields;
|
|
2157
|
+
_formState.isDirty = !isEmptyObject(newDirtyFields);
|
|
2158
|
+
}
|
|
2159
|
+
if (!options.keepIsValid) {
|
|
2160
|
+
_setValid();
|
|
2161
|
+
}
|
|
2162
|
+
_subjects.state.next({
|
|
2163
|
+
..._formState,
|
|
2164
|
+
defaultValues: _defaultValues,
|
|
2165
|
+
});
|
|
2166
|
+
};
|
|
2026
2167
|
const methods = {
|
|
2027
2168
|
control: {
|
|
2028
2169
|
register,
|
|
@@ -2079,6 +2220,8 @@ function createFormControl(props = {}) {
|
|
|
2079
2220
|
..._options,
|
|
2080
2221
|
...value,
|
|
2081
2222
|
};
|
|
2223
|
+
_validationModeBeforeSubmit = getValidationModes(_options.mode);
|
|
2224
|
+
_validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
|
|
2082
2225
|
},
|
|
2083
2226
|
},
|
|
2084
2227
|
subscribe,
|
|
@@ -2091,6 +2234,7 @@ function createFormControl(props = {}) {
|
|
|
2091
2234
|
getValues,
|
|
2092
2235
|
reset,
|
|
2093
2236
|
resetField,
|
|
2237
|
+
resetDefaultValues,
|
|
2094
2238
|
clearErrors,
|
|
2095
2239
|
unregister,
|
|
2096
2240
|
setError,
|