react-hook-form 8.0.0-beta.0 → 8.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -29
- package/dist/constants.d.ts +5 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/form.d.ts.map +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.mjs +541 -217
- 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 +13 -0
- package/dist/logic/createFormControl.d.ts.map +1 -1
- package/dist/logic/getDirtyFields.d.ts.map +1 -1
- package/dist/logic/{isNameInFieldArray.d.ts → getFieldArrayParentNames.d.ts} +2 -2
- package/dist/logic/getFieldArrayParentNames.d.ts.map +1 -0
- package/dist/logic/getProxyFormState.d.ts.map +1 -1
- package/dist/logic/getResolverOptions.d.ts +2 -2
- package/dist/logic/hasValidation.d.ts +1 -1
- package/dist/logic/shouldRenderFormState.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 +362 -92
- package/dist/react-server.esm.mjs.map +1 -1
- package/dist/types/errors.d.ts +1 -0
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/fieldArray.d.ts +1 -2
- package/dist/types/fieldArray.d.ts.map +1 -1
- package/dist/types/form.d.ts +14 -41
- package/dist/types/form.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/path/eager.d.ts +1 -1
- package/dist/types/path/eager.d.ts.map +1 -1
- package/dist/types/utils.d.ts +5 -4
- package/dist/types/utils.d.ts.map +1 -1
- package/dist/types/validator.d.ts +13 -1
- package/dist/types/validator.d.ts.map +1 -1
- package/dist/types/watch.d.ts +32 -0
- package/dist/types/watch.d.ts.map +1 -0
- package/dist/useController.d.ts.map +1 -1
- package/dist/useFieldArray.d.ts +2 -2
- package/dist/useFieldArray.d.ts.map +1 -1
- package/dist/useForm.d.ts.map +1 -1
- package/dist/useFormContext.d.ts.map +1 -1
- package/dist/useFormControlContext.d.ts +12 -0
- package/dist/useFormControlContext.d.ts.map +1 -0
- package/dist/useFormState.d.ts.map +1 -1
- package/dist/useWatch.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/unset.d.ts.map +1 -1
- package/dist/watch.d.ts +7 -17
- package/dist/watch.d.ts.map +1 -1
- package/package.json +29 -29
- package/dist/logic/isNameInFieldArray.d.ts.map +0 -1
|
@@ -10,7 +10,11 @@ var appendErrors = (name, validateAllFieldCriteria, errors, type, message) => va
|
|
|
10
10
|
|
|
11
11
|
const EVENTS = {
|
|
12
12
|
BLUR: 'blur',
|
|
13
|
-
FOCUS_OUT: 'focusout'
|
|
13
|
+
FOCUS_OUT: 'focusout',
|
|
14
|
+
SUBMIT: 'submit',
|
|
15
|
+
TRIGGER: 'trigger',
|
|
16
|
+
VALID: 'valid',
|
|
17
|
+
};
|
|
14
18
|
const VALIDATION_MODE = {
|
|
15
19
|
onBlur: 'onBlur',
|
|
16
20
|
onChange: 'onChange',
|
|
@@ -27,6 +31,8 @@ const INPUT_VALIDATION_RULES = {
|
|
|
27
31
|
required: 'required',
|
|
28
32
|
validate: 'validate',
|
|
29
33
|
};
|
|
34
|
+
const FORM_ERROR_TYPE = 'form';
|
|
35
|
+
const ROOT_ERROR_TYPE = 'root';
|
|
30
36
|
|
|
31
37
|
var isDateObject = (value) => value instanceof Date;
|
|
32
38
|
|
|
@@ -102,34 +108,37 @@ var createSubject = () => {
|
|
|
102
108
|
|
|
103
109
|
var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
|
|
104
110
|
|
|
105
|
-
function deepEqual(object1, object2,
|
|
111
|
+
function deepEqual(object1, object2, visited = new WeakSet()) {
|
|
112
|
+
if (object1 === object2) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
106
115
|
if (isPrimitive(object1) || isPrimitive(object2)) {
|
|
107
116
|
return Object.is(object1, object2);
|
|
108
117
|
}
|
|
109
118
|
if (isDateObject(object1) && isDateObject(object2)) {
|
|
110
|
-
return object1.getTime()
|
|
119
|
+
return Object.is(object1.getTime(), object2.getTime());
|
|
111
120
|
}
|
|
112
121
|
const keys1 = Object.keys(object1);
|
|
113
122
|
const keys2 = Object.keys(object2);
|
|
114
123
|
if (keys1.length !== keys2.length) {
|
|
115
124
|
return false;
|
|
116
125
|
}
|
|
117
|
-
if (
|
|
126
|
+
if (visited.has(object1) || visited.has(object2)) {
|
|
118
127
|
return true;
|
|
119
128
|
}
|
|
120
|
-
|
|
121
|
-
|
|
129
|
+
visited.add(object1);
|
|
130
|
+
visited.add(object2);
|
|
122
131
|
for (const key of keys1) {
|
|
123
132
|
const val1 = object1[key];
|
|
124
|
-
if (!
|
|
133
|
+
if (!(key in object2)) {
|
|
125
134
|
return false;
|
|
126
135
|
}
|
|
127
136
|
if (key !== 'ref') {
|
|
128
137
|
const val2 = object2[key];
|
|
129
138
|
if ((isDateObject(val1) && isDateObject(val2)) ||
|
|
130
|
-
(isObject(val1)
|
|
131
|
-
|
|
132
|
-
? !deepEqual(val1, val2,
|
|
139
|
+
((isObject(val1) || Array.isArray(val1)) &&
|
|
140
|
+
(isObject(val2) || Array.isArray(val2)))
|
|
141
|
+
? !deepEqual(val1, val2, visited)
|
|
133
142
|
: !Object.is(val1, val2)) {
|
|
134
143
|
return false;
|
|
135
144
|
}
|
|
@@ -168,7 +177,10 @@ var get = (object, path, defaultValue) => {
|
|
|
168
177
|
if (!path || !isObject(object)) {
|
|
169
178
|
return defaultValue;
|
|
170
179
|
}
|
|
171
|
-
const
|
|
180
|
+
const paths = isKey(path) ? [path] : stringToPath(path);
|
|
181
|
+
const result = paths.reduce((result, key) => {
|
|
182
|
+
return isNullOrUndefined(result) ? undefined : result[key];
|
|
183
|
+
}, object);
|
|
172
184
|
return isUndefined(result) || result === object
|
|
173
185
|
? isUndefined(object[path])
|
|
174
186
|
? defaultValue
|
|
@@ -234,7 +246,12 @@ function baseGet(object, updatePath) {
|
|
|
234
246
|
const length = updatePath.slice(0, -1).length;
|
|
235
247
|
let index = 0;
|
|
236
248
|
while (index < length) {
|
|
237
|
-
|
|
249
|
+
if (isNullOrUndefined(object)) {
|
|
250
|
+
object = undefined;
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
object = object[updatePath[index]];
|
|
254
|
+
index++;
|
|
238
255
|
}
|
|
239
256
|
return object;
|
|
240
257
|
}
|
|
@@ -247,6 +264,10 @@ function isEmptyArray(obj) {
|
|
|
247
264
|
return true;
|
|
248
265
|
}
|
|
249
266
|
function unset(object, path) {
|
|
267
|
+
if (isString(path) && Object.prototype.hasOwnProperty.call(object, path)) {
|
|
268
|
+
delete object[path];
|
|
269
|
+
return object;
|
|
270
|
+
}
|
|
250
271
|
const paths = Array.isArray(path)
|
|
251
272
|
? path
|
|
252
273
|
: isKey(path)
|
|
@@ -304,6 +325,29 @@ function markFieldsDirty(data, fields = {}) {
|
|
|
304
325
|
}
|
|
305
326
|
return fields;
|
|
306
327
|
}
|
|
328
|
+
function pruneDirtyFields(value) {
|
|
329
|
+
if (value === false) {
|
|
330
|
+
return undefined;
|
|
331
|
+
}
|
|
332
|
+
if (value === true) {
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
if (Array.isArray(value)) {
|
|
336
|
+
const result = value.map((value) => pruneDirtyFields(value));
|
|
337
|
+
return (result.some((value) => value !== undefined) ? result : undefined);
|
|
338
|
+
}
|
|
339
|
+
if (isObject(value)) {
|
|
340
|
+
const result = {};
|
|
341
|
+
for (const key in value) {
|
|
342
|
+
const pruned = pruneDirtyFields(value[key]);
|
|
343
|
+
if (!isUndefined(pruned)) {
|
|
344
|
+
result[key] = pruned;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return (Object.keys(result).length ? result : undefined);
|
|
348
|
+
}
|
|
349
|
+
return undefined;
|
|
350
|
+
}
|
|
307
351
|
function getDirtyFields(data, formValues, dirtyFieldsFromValues) {
|
|
308
352
|
if (!dirtyFieldsFromValues) {
|
|
309
353
|
dirtyFieldsFromValues = markFieldsDirty(formValues);
|
|
@@ -323,7 +367,7 @@ function getDirtyFields(data, formValues, dirtyFieldsFromValues) {
|
|
|
323
367
|
dirtyFieldsFromValues[key] = !deepEqual(value, formValue);
|
|
324
368
|
}
|
|
325
369
|
}
|
|
326
|
-
return dirtyFieldsFromValues;
|
|
370
|
+
return pruneDirtyFields(dirtyFieldsFromValues) || {};
|
|
327
371
|
}
|
|
328
372
|
|
|
329
373
|
var getEventValue = (event) => isObject(event) && event.target
|
|
@@ -332,6 +376,16 @@ var getEventValue = (event) => isObject(event) && event.target
|
|
|
332
376
|
: event.target.value
|
|
333
377
|
: event;
|
|
334
378
|
|
|
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
|
+
|
|
335
389
|
const defaultResult = {
|
|
336
390
|
value: false,
|
|
337
391
|
isValid: false,
|
|
@@ -452,10 +506,6 @@ var hasValidation = (options) => options.mount &&
|
|
|
452
506
|
options.pattern ||
|
|
453
507
|
options.validate);
|
|
454
508
|
|
|
455
|
-
var getNodeParentName = (name) => name.substring(0, name.search(/\.\d+(\.|$)/)) || name;
|
|
456
|
-
|
|
457
|
-
var isNameInFieldArray = (names, name) => names.has(getNodeParentName(name));
|
|
458
|
-
|
|
459
509
|
var isWatched = (name, _names, isBlurEvent) => !isBlurEvent &&
|
|
460
510
|
(_names.watchAll ||
|
|
461
511
|
_names.watch.has(name) ||
|
|
@@ -529,7 +579,8 @@ var shouldRenderFormState = (formStateData, _proxyFormState, updateFormState, is
|
|
|
529
579
|
updateFormState(formStateData);
|
|
530
580
|
const { name, ...formState } = formStateData;
|
|
531
581
|
return (isEmptyObject(formState) ||
|
|
532
|
-
|
|
582
|
+
(isRoot &&
|
|
583
|
+
Object.keys(formState).length >= Object.keys(_proxyFormState).length) ||
|
|
533
584
|
Object.keys(formState).find((key) => _proxyFormState[key] ===
|
|
534
585
|
(!isRoot || VALIDATION_MODE.all)));
|
|
535
586
|
};
|
|
@@ -563,7 +614,7 @@ var unsetEmptyArray = (ref, name) => !compact(get(ref, name)).length && unset(re
|
|
|
563
614
|
|
|
564
615
|
var updateFieldArrayRootError = (errors, error, name) => {
|
|
565
616
|
const fieldArrayErrors = convertToArrayPayload(get(errors, name));
|
|
566
|
-
set(fieldArrayErrors,
|
|
617
|
+
set(fieldArrayErrors, ROOT_ERROR_TYPE, error[name]);
|
|
567
618
|
set(errors, name, fieldArrayErrors);
|
|
568
619
|
return errors;
|
|
569
620
|
};
|
|
@@ -609,7 +660,8 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
|
|
|
609
660
|
isUndefined(inputValue)) ||
|
|
610
661
|
(isHTMLElement(ref) && ref.value === '') ||
|
|
611
662
|
inputValue === '' ||
|
|
612
|
-
(Array.isArray(inputValue) && !inputValue.length)
|
|
663
|
+
(Array.isArray(inputValue) && !inputValue.length) ||
|
|
664
|
+
(valueAsNumber && typeof inputValue === 'number' && isNaN(inputValue));
|
|
613
665
|
const appendErrorsCurry = appendErrors.bind(null, name, validateAllFieldCriteria, error);
|
|
614
666
|
const getMinMaxMessage = (exceedMax, maxLengthMessage, minLengthMessage, maxType = INPUT_VALIDATION_RULES.maxLength, minType = INPUT_VALIDATION_RULES.minLength) => {
|
|
615
667
|
const message = exceedMax ? maxLengthMessage : minLengthMessage;
|
|
@@ -771,24 +823,27 @@ const defaultOptions = {
|
|
|
771
823
|
reValidateMode: VALIDATION_MODE.onChange,
|
|
772
824
|
shouldFocusError: true,
|
|
773
825
|
};
|
|
826
|
+
const DEFAULT_FORM_STATE = {
|
|
827
|
+
submitCount: 0,
|
|
828
|
+
isDirty: false,
|
|
829
|
+
isReady: false,
|
|
830
|
+
isValidating: false,
|
|
831
|
+
isSubmitted: false,
|
|
832
|
+
isSubmitting: false,
|
|
833
|
+
isSubmitSuccessful: false,
|
|
834
|
+
isValid: false,
|
|
835
|
+
touchedFields: {},
|
|
836
|
+
dirtyFields: {},
|
|
837
|
+
validatingFields: {},
|
|
838
|
+
};
|
|
774
839
|
function createFormControl(props = {}) {
|
|
775
840
|
let _options = {
|
|
776
841
|
...defaultOptions,
|
|
777
842
|
...props,
|
|
778
843
|
};
|
|
779
844
|
let _formState = {
|
|
780
|
-
|
|
781
|
-
isDirty: false,
|
|
782
|
-
isReady: false,
|
|
845
|
+
...cloneObject(DEFAULT_FORM_STATE),
|
|
783
846
|
isLoading: isFunction(_options.defaultValues),
|
|
784
|
-
isValidating: false,
|
|
785
|
-
isSubmitted: false,
|
|
786
|
-
isSubmitting: false,
|
|
787
|
-
isSubmitSuccessful: false,
|
|
788
|
-
isValid: false,
|
|
789
|
-
touchedFields: {},
|
|
790
|
-
dirtyFields: {},
|
|
791
|
-
validatingFields: {},
|
|
792
847
|
errors: _options.errors || {},
|
|
793
848
|
disabled: _options.disabled || false,
|
|
794
849
|
};
|
|
@@ -803,6 +858,7 @@ function createFormControl(props = {}) {
|
|
|
803
858
|
action: false,
|
|
804
859
|
mount: false,
|
|
805
860
|
watch: false,
|
|
861
|
+
keepIsValid: false,
|
|
806
862
|
};
|
|
807
863
|
let _names = {
|
|
808
864
|
mount: new Set(),
|
|
@@ -810,10 +866,11 @@ function createFormControl(props = {}) {
|
|
|
810
866
|
unMount: new Set(),
|
|
811
867
|
array: new Set(),
|
|
812
868
|
watch: new Set(),
|
|
869
|
+
registerName: new Set(),
|
|
813
870
|
};
|
|
814
871
|
let delayErrorCallback;
|
|
815
872
|
let timer = 0;
|
|
816
|
-
const
|
|
873
|
+
const defaultProxyFormState = {
|
|
817
874
|
isDirty: false,
|
|
818
875
|
dirtyFields: false,
|
|
819
876
|
validatingFields: false,
|
|
@@ -822,6 +879,9 @@ function createFormControl(props = {}) {
|
|
|
822
879
|
isValid: false,
|
|
823
880
|
errors: false,
|
|
824
881
|
};
|
|
882
|
+
const _proxyFormState = {
|
|
883
|
+
...defaultProxyFormState,
|
|
884
|
+
};
|
|
825
885
|
let _proxySubscribeFormState = {
|
|
826
886
|
..._proxyFormState,
|
|
827
887
|
};
|
|
@@ -835,13 +895,25 @@ function createFormControl(props = {}) {
|
|
|
835
895
|
timer = setTimeout(callback, wait);
|
|
836
896
|
};
|
|
837
897
|
const _setValid = async (shouldUpdateValid) => {
|
|
898
|
+
if (_state.keepIsValid) {
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
838
901
|
if (!_options.disabled &&
|
|
839
902
|
(_proxyFormState.isValid ||
|
|
840
903
|
_proxySubscribeFormState.isValid ||
|
|
841
904
|
shouldUpdateValid)) {
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
905
|
+
let isValid;
|
|
906
|
+
if (_options.resolver) {
|
|
907
|
+
isValid = isEmptyObject((await _runSchema()).errors);
|
|
908
|
+
_updateIsValidating();
|
|
909
|
+
}
|
|
910
|
+
else {
|
|
911
|
+
isValid = await executeBuiltInValidation({
|
|
912
|
+
fields: _fields,
|
|
913
|
+
onlyCheckValid: true,
|
|
914
|
+
eventType: EVENTS.VALID,
|
|
915
|
+
});
|
|
916
|
+
}
|
|
845
917
|
if (isValid !== _formState.isValid) {
|
|
846
918
|
_subjects.state.next({
|
|
847
919
|
isValid,
|
|
@@ -868,6 +940,9 @@ function createFormControl(props = {}) {
|
|
|
868
940
|
});
|
|
869
941
|
}
|
|
870
942
|
};
|
|
943
|
+
const _updateDirtyFields = () => {
|
|
944
|
+
_formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
|
|
945
|
+
};
|
|
871
946
|
const _setFieldArray = (name, values = [], method, args, shouldSetValues = true, shouldUpdateFieldsAndState = true) => {
|
|
872
947
|
if (args && method && !_options.disabled) {
|
|
873
948
|
_state.action = true;
|
|
@@ -889,7 +964,7 @@ function createFormControl(props = {}) {
|
|
|
889
964
|
shouldSetValues && set(_formState.touchedFields, name, touchedFields);
|
|
890
965
|
}
|
|
891
966
|
if (_proxyFormState.dirtyFields || _proxySubscribeFormState.dirtyFields) {
|
|
892
|
-
|
|
967
|
+
_updateDirtyFields();
|
|
893
968
|
}
|
|
894
969
|
_subjects.state.next({
|
|
895
970
|
name,
|
|
@@ -916,16 +991,53 @@ function createFormControl(props = {}) {
|
|
|
916
991
|
isValid: false,
|
|
917
992
|
});
|
|
918
993
|
};
|
|
994
|
+
const hasExplicitNullIntermediate = (name) => {
|
|
995
|
+
const segments = isKey(name) ? [name] : stringToPath(name);
|
|
996
|
+
let formValues = _formValues;
|
|
997
|
+
let defaultValues = _defaultValues;
|
|
998
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
999
|
+
const key = segments[i];
|
|
1000
|
+
formValues = isNullOrUndefined(formValues) ? formValues : formValues[key];
|
|
1001
|
+
defaultValues = isNullOrUndefined(defaultValues)
|
|
1002
|
+
? defaultValues
|
|
1003
|
+
: defaultValues[key];
|
|
1004
|
+
if (formValues === null && defaultValues !== null) {
|
|
1005
|
+
return true;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
return false;
|
|
1009
|
+
};
|
|
919
1010
|
const updateValidAndValue = (name, shouldSkipSetValueAs, value, ref) => {
|
|
920
1011
|
const field = get(_fields, name);
|
|
921
1012
|
if (field) {
|
|
1013
|
+
if (hasExplicitNullIntermediate(name)) {
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
const wasUnsetInFormValues = isUndefined(get(_formValues, name));
|
|
922
1017
|
const defaultValue = get(_formValues, name, isUndefined(value) ? get(_defaultValues, name) : value);
|
|
923
1018
|
isUndefined(defaultValue) ||
|
|
924
1019
|
(ref && ref.defaultChecked) ||
|
|
925
1020
|
shouldSkipSetValueAs
|
|
926
1021
|
? set(_formValues, name, shouldSkipSetValueAs ? defaultValue : getFieldValue(field._f))
|
|
927
1022
|
: setFieldValue(name, defaultValue);
|
|
928
|
-
_state.mount && !_state.action
|
|
1023
|
+
if (_state.mount && !_state.action) {
|
|
1024
|
+
_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
|
+
if (wasUnsetInFormValues &&
|
|
1032
|
+
_formState.isDirty &&
|
|
1033
|
+
(_proxyFormState.isDirty || _proxySubscribeFormState.isDirty)) {
|
|
1034
|
+
const isDirty = _getDirty();
|
|
1035
|
+
if (!isDirty) {
|
|
1036
|
+
_formState.isDirty = false;
|
|
1037
|
+
_subjects.state.next({ ..._formState });
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
929
1041
|
}
|
|
930
1042
|
};
|
|
931
1043
|
const updateTouchAndDirty = (name, fieldValue, isBlurEvent, shouldDirty, shouldRender) => {
|
|
@@ -943,9 +1055,14 @@ function createFormControl(props = {}) {
|
|
|
943
1055
|
}
|
|
944
1056
|
const isCurrentFieldPristine = deepEqual(get(_defaultValues, name), fieldValue);
|
|
945
1057
|
isPreviousDirty = !!get(_formState.dirtyFields, name);
|
|
946
|
-
isCurrentFieldPristine
|
|
947
|
-
|
|
948
|
-
|
|
1058
|
+
if (isCurrentFieldPristine !== _formState.isDirty) {
|
|
1059
|
+
_formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
|
|
1060
|
+
}
|
|
1061
|
+
else {
|
|
1062
|
+
isCurrentFieldPristine
|
|
1063
|
+
? unset(_formState.dirtyFields, name)
|
|
1064
|
+
: set(_formState.dirtyFields, name, true);
|
|
1065
|
+
}
|
|
949
1066
|
output.dirtyFields = _formState.dirtyFields;
|
|
950
1067
|
shouldUpdateField =
|
|
951
1068
|
shouldUpdateField ||
|
|
@@ -1003,17 +1120,18 @@ function createFormControl(props = {}) {
|
|
|
1003
1120
|
};
|
|
1004
1121
|
const _runSchema = async (name) => {
|
|
1005
1122
|
_updateIsValidating(name, true);
|
|
1006
|
-
|
|
1007
|
-
_updateIsValidating(name);
|
|
1008
|
-
return result;
|
|
1123
|
+
return await _options.resolver(_formValues, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation));
|
|
1009
1124
|
};
|
|
1010
1125
|
const executeSchemaAndUpdateState = async (names) => {
|
|
1011
1126
|
const { errors } = await _runSchema(names);
|
|
1127
|
+
_updateIsValidating(names);
|
|
1012
1128
|
if (names) {
|
|
1013
1129
|
for (const name of names) {
|
|
1014
1130
|
const error = get(errors, name);
|
|
1015
1131
|
error
|
|
1016
|
-
?
|
|
1132
|
+
? _names.array.has(name) && isObject(error)
|
|
1133
|
+
? updateFieldArrayRootError(_formState.errors, { [name]: error }, name)
|
|
1134
|
+
: set(_formState.errors, name, error)
|
|
1017
1135
|
: unset(_formState.errors, name);
|
|
1018
1136
|
}
|
|
1019
1137
|
}
|
|
@@ -1022,9 +1140,55 @@ function createFormControl(props = {}) {
|
|
|
1022
1140
|
}
|
|
1023
1141
|
return errors;
|
|
1024
1142
|
};
|
|
1025
|
-
const
|
|
1143
|
+
const validateForm = async ({ name, eventType, }) => {
|
|
1144
|
+
if (props.validate) {
|
|
1145
|
+
const result = await props.validate({
|
|
1146
|
+
formValues: _formValues,
|
|
1147
|
+
formState: _formState,
|
|
1148
|
+
name,
|
|
1149
|
+
eventType,
|
|
1150
|
+
});
|
|
1151
|
+
if (isObject(result)) {
|
|
1152
|
+
for (const key in result) {
|
|
1153
|
+
const error = result[key];
|
|
1154
|
+
if (error) {
|
|
1155
|
+
setError(`${FORM_ERROR_TYPE}.${key}`, {
|
|
1156
|
+
message: isString(error.message) ? error.message : '',
|
|
1157
|
+
type: error.type || INPUT_VALIDATION_RULES.validate,
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
else if (isString(result) || !result) {
|
|
1163
|
+
setError(FORM_ERROR_TYPE, {
|
|
1164
|
+
message: result || '',
|
|
1165
|
+
type: INPUT_VALIDATION_RULES.validate,
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
else {
|
|
1169
|
+
clearErrors(FORM_ERROR_TYPE);
|
|
1170
|
+
}
|
|
1171
|
+
return result;
|
|
1172
|
+
}
|
|
1173
|
+
return true;
|
|
1174
|
+
};
|
|
1175
|
+
const executeBuiltInValidation = async ({ fields, onlyCheckValid, name, eventType, context = {
|
|
1026
1176
|
valid: true,
|
|
1027
|
-
|
|
1177
|
+
runRootValidation: false,
|
|
1178
|
+
}, }) => {
|
|
1179
|
+
if (props.validate) {
|
|
1180
|
+
context.runRootValidation = true;
|
|
1181
|
+
const result = await validateForm({
|
|
1182
|
+
name,
|
|
1183
|
+
eventType,
|
|
1184
|
+
});
|
|
1185
|
+
if (!result) {
|
|
1186
|
+
context.valid = false;
|
|
1187
|
+
if (onlyCheckValid) {
|
|
1188
|
+
return context.valid;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1028
1192
|
for (const name in fields) {
|
|
1029
1193
|
const field = fields[name];
|
|
1030
1194
|
if (field) {
|
|
@@ -1032,28 +1196,41 @@ function createFormControl(props = {}) {
|
|
|
1032
1196
|
if (_f) {
|
|
1033
1197
|
const isFieldArrayRoot = _names.array.has(_f.name);
|
|
1034
1198
|
const isPromiseFunction = field._f && hasPromiseValidation(field._f);
|
|
1035
|
-
|
|
1199
|
+
const shouldTrackIsValidatingState = _proxyFormState.validatingFields ||
|
|
1200
|
+
_proxyFormState.isValidating ||
|
|
1201
|
+
_proxySubscribeFormState.validatingFields ||
|
|
1202
|
+
_proxySubscribeFormState.isValidating;
|
|
1203
|
+
if (isPromiseFunction && shouldTrackIsValidatingState) {
|
|
1036
1204
|
_updateIsValidating([_f.name], true);
|
|
1037
1205
|
}
|
|
1038
|
-
const fieldError = await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !
|
|
1039
|
-
if (isPromiseFunction &&
|
|
1206
|
+
const fieldError = await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !onlyCheckValid, isFieldArrayRoot);
|
|
1207
|
+
if (isPromiseFunction && shouldTrackIsValidatingState) {
|
|
1040
1208
|
_updateIsValidating([_f.name]);
|
|
1041
1209
|
}
|
|
1042
1210
|
if (fieldError[_f.name]) {
|
|
1043
1211
|
context.valid = false;
|
|
1044
|
-
if (
|
|
1212
|
+
if (onlyCheckValid) {
|
|
1045
1213
|
break;
|
|
1046
1214
|
}
|
|
1047
1215
|
}
|
|
1048
|
-
!
|
|
1216
|
+
!onlyCheckValid &&
|
|
1049
1217
|
(get(fieldError, _f.name)
|
|
1050
1218
|
? isFieldArrayRoot
|
|
1051
1219
|
? updateFieldArrayRootError(_formState.errors, fieldError, _f.name)
|
|
1052
1220
|
: set(_formState.errors, _f.name, fieldError[_f.name])
|
|
1053
1221
|
: unset(_formState.errors, _f.name));
|
|
1222
|
+
if (props.shouldUseNativeValidation && fieldError[_f.name]) {
|
|
1223
|
+
break;
|
|
1224
|
+
}
|
|
1054
1225
|
}
|
|
1055
1226
|
!isEmptyObject(fieldValue) &&
|
|
1056
|
-
(await executeBuiltInValidation(
|
|
1227
|
+
(await executeBuiltInValidation({
|
|
1228
|
+
context,
|
|
1229
|
+
onlyCheckValid,
|
|
1230
|
+
fields: fieldValue,
|
|
1231
|
+
name: name,
|
|
1232
|
+
eventType,
|
|
1233
|
+
}));
|
|
1057
1234
|
}
|
|
1058
1235
|
}
|
|
1059
1236
|
return context.valid;
|
|
@@ -1133,7 +1310,7 @@ function createFormControl(props = {}) {
|
|
|
1133
1310
|
updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty, true);
|
|
1134
1311
|
options.shouldValidate && trigger(name);
|
|
1135
1312
|
};
|
|
1136
|
-
const
|
|
1313
|
+
const setFieldValues = (name, value, options) => {
|
|
1137
1314
|
for (const fieldKey in value) {
|
|
1138
1315
|
if (!value.hasOwnProperty(fieldKey)) {
|
|
1139
1316
|
return;
|
|
@@ -1145,7 +1322,7 @@ function createFormControl(props = {}) {
|
|
|
1145
1322
|
isObject(fieldValue) ||
|
|
1146
1323
|
(field && !field._f)) &&
|
|
1147
1324
|
!isDateObject(fieldValue)
|
|
1148
|
-
?
|
|
1325
|
+
? setFieldValues(fieldName, fieldValue, options)
|
|
1149
1326
|
: setFieldValue(fieldName, fieldValue, options);
|
|
1150
1327
|
}
|
|
1151
1328
|
};
|
|
@@ -1153,7 +1330,11 @@ function createFormControl(props = {}) {
|
|
|
1153
1330
|
const field = get(_fields, name);
|
|
1154
1331
|
const isFieldArray = _names.array.has(name);
|
|
1155
1332
|
const cloneValue = cloneObject(value);
|
|
1156
|
-
|
|
1333
|
+
const previousValue = get(_formValues, name);
|
|
1334
|
+
const isValueUnchanged = deepEqual(previousValue, cloneValue);
|
|
1335
|
+
if (!isValueUnchanged) {
|
|
1336
|
+
set(_formValues, name, cloneValue);
|
|
1337
|
+
}
|
|
1157
1338
|
if (isFieldArray) {
|
|
1158
1339
|
_subjects.array.next({
|
|
1159
1340
|
name,
|
|
@@ -1164,23 +1345,53 @@ function createFormControl(props = {}) {
|
|
|
1164
1345
|
_proxySubscribeFormState.isDirty ||
|
|
1165
1346
|
_proxySubscribeFormState.dirtyFields) &&
|
|
1166
1347
|
options.shouldDirty) {
|
|
1348
|
+
_updateDirtyFields();
|
|
1167
1349
|
_subjects.state.next({
|
|
1168
1350
|
name,
|
|
1169
|
-
dirtyFields:
|
|
1351
|
+
dirtyFields: _formState.dirtyFields,
|
|
1170
1352
|
isDirty: _getDirty(name, cloneValue),
|
|
1171
1353
|
});
|
|
1172
1354
|
}
|
|
1173
1355
|
}
|
|
1174
1356
|
else {
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1357
|
+
const isEmpty = (Array.isArray(cloneValue) && !cloneValue.length) ||
|
|
1358
|
+
isEmptyObject(cloneValue);
|
|
1359
|
+
if (!field || field._f || isNullOrUndefined(cloneValue) || isEmpty) {
|
|
1360
|
+
setFieldValue(name, cloneValue, options);
|
|
1361
|
+
}
|
|
1362
|
+
else {
|
|
1363
|
+
setFieldValues(name, cloneValue, options);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
if (!isValueUnchanged) {
|
|
1367
|
+
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
|
+
}
|
|
1374
|
+
_subjects.state.next({
|
|
1375
|
+
...(watched && _formState),
|
|
1376
|
+
name: _state.mount || watched ? name : undefined,
|
|
1377
|
+
values,
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
const setValues = (formValues) => {
|
|
1382
|
+
const updatedFormValues = isFunction(formValues)
|
|
1383
|
+
? formValues(_formValues)
|
|
1384
|
+
: formValues;
|
|
1385
|
+
if (!deepEqual(_formValues, updatedFormValues)) {
|
|
1386
|
+
_formValues = {
|
|
1387
|
+
..._formValues,
|
|
1388
|
+
...updatedFormValues,
|
|
1389
|
+
};
|
|
1390
|
+
for (const fieldName of _names.mount) {
|
|
1391
|
+
setValue(fieldName, get(updatedFormValues, fieldName));
|
|
1392
|
+
}
|
|
1393
|
+
_subjects.state.next({ ..._formState, values: _formValues });
|
|
1178
1394
|
}
|
|
1179
|
-
isWatched(name, _names) && _subjects.state.next({ ..._formState, name });
|
|
1180
|
-
_subjects.state.next({
|
|
1181
|
-
name: _state.mount ? name : undefined,
|
|
1182
|
-
values: cloneObject(_formValues),
|
|
1183
|
-
});
|
|
1184
1395
|
};
|
|
1185
1396
|
const onChange = async (event) => {
|
|
1186
1397
|
_state.mount = true;
|
|
@@ -1204,6 +1415,7 @@ function createFormControl(props = {}) {
|
|
|
1204
1415
|
: getEventValue(event);
|
|
1205
1416
|
const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
|
|
1206
1417
|
const shouldSkipValidation = (!hasValidation(field._f) &&
|
|
1418
|
+
!props.validate &&
|
|
1207
1419
|
!_options.resolver &&
|
|
1208
1420
|
!get(_formState.errors, name) &&
|
|
1209
1421
|
!field._f.deps) ||
|
|
@@ -1241,9 +1453,16 @@ function createFormControl(props = {}) {
|
|
|
1241
1453
|
return (shouldRender &&
|
|
1242
1454
|
_subjects.state.next({ name, ...(watched ? {} : fieldState) }));
|
|
1243
1455
|
}
|
|
1456
|
+
if (!_options.resolver && props.validate) {
|
|
1457
|
+
await validateForm({
|
|
1458
|
+
name: name,
|
|
1459
|
+
eventType: event.type,
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1244
1462
|
!isBlurEvent && watched && _subjects.state.next({ ..._formState });
|
|
1245
1463
|
if (_options.resolver) {
|
|
1246
1464
|
const { errors } = await _runSchema([name]);
|
|
1465
|
+
_updateIsValidating([name]);
|
|
1247
1466
|
_updateIsFieldValueUpdated(fieldValue);
|
|
1248
1467
|
if (isFieldValueUpdated) {
|
|
1249
1468
|
const previousErrorLookupResult = schemaErrorLookup(_formState.errors, _fields, name);
|
|
@@ -1264,7 +1483,12 @@ function createFormControl(props = {}) {
|
|
|
1264
1483
|
}
|
|
1265
1484
|
else if (_proxyFormState.isValid ||
|
|
1266
1485
|
_proxySubscribeFormState.isValid) {
|
|
1267
|
-
isValid = await executeBuiltInValidation(
|
|
1486
|
+
isValid = await executeBuiltInValidation({
|
|
1487
|
+
fields: _fields,
|
|
1488
|
+
onlyCheckValid: true,
|
|
1489
|
+
name: name,
|
|
1490
|
+
eventType: event.type,
|
|
1491
|
+
});
|
|
1268
1492
|
}
|
|
1269
1493
|
}
|
|
1270
1494
|
}
|
|
@@ -1297,12 +1521,19 @@ function createFormControl(props = {}) {
|
|
|
1297
1521
|
else if (name) {
|
|
1298
1522
|
validationResult = (await Promise.all(fieldNames.map(async (fieldName) => {
|
|
1299
1523
|
const field = get(_fields, fieldName);
|
|
1300
|
-
return await executeBuiltInValidation(
|
|
1524
|
+
return await executeBuiltInValidation({
|
|
1525
|
+
fields: field && field._f ? { [fieldName]: field } : field,
|
|
1526
|
+
eventType: EVENTS.TRIGGER,
|
|
1527
|
+
});
|
|
1301
1528
|
}))).every(Boolean);
|
|
1302
1529
|
!(!validationResult && !_formState.isValid) && _setValid();
|
|
1303
1530
|
}
|
|
1304
1531
|
else {
|
|
1305
|
-
validationResult = isValid = await executeBuiltInValidation(
|
|
1532
|
+
validationResult = isValid = await executeBuiltInValidation({
|
|
1533
|
+
fields: _fields,
|
|
1534
|
+
name,
|
|
1535
|
+
eventType: EVENTS.TRIGGER,
|
|
1536
|
+
});
|
|
1306
1537
|
}
|
|
1307
1538
|
_subjects.state.next({
|
|
1308
1539
|
...(!isString(name) ||
|
|
@@ -1339,11 +1570,24 @@ function createFormControl(props = {}) {
|
|
|
1339
1570
|
isTouched: !!get((formState || _formState).touchedFields, name),
|
|
1340
1571
|
});
|
|
1341
1572
|
const clearErrors = (name) => {
|
|
1342
|
-
name
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1573
|
+
const names = name ? convertToArrayPayload(name) : undefined;
|
|
1574
|
+
names?.forEach((inputName) => unset(_formState.errors, inputName));
|
|
1575
|
+
if (names) {
|
|
1576
|
+
// Emit for each cleared field with the field name so that
|
|
1577
|
+
// shouldSubscribeByName can filter and avoid broad re-renders
|
|
1578
|
+
names.forEach((inputName) => {
|
|
1579
|
+
_subjects.state.next({
|
|
1580
|
+
name: inputName,
|
|
1581
|
+
errors: _formState.errors,
|
|
1582
|
+
});
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1585
|
+
else {
|
|
1586
|
+
// Clear all errors - emit without name to notify all subscribers
|
|
1587
|
+
_subjects.state.next({
|
|
1588
|
+
errors: {},
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1347
1591
|
};
|
|
1348
1592
|
const setError = (name, error, options) => {
|
|
1349
1593
|
const ref = (get(_fields, name, { _f: {} })._f || {}).ref;
|
|
@@ -1362,18 +1606,14 @@ function createFormControl(props = {}) {
|
|
|
1362
1606
|
});
|
|
1363
1607
|
options && options.shouldFocus && ref && ref.focus && ref.focus();
|
|
1364
1608
|
};
|
|
1365
|
-
const watch = (name, defaultValue) =>
|
|
1366
|
-
? _subjects.state.subscribe({
|
|
1367
|
-
next: (payload) => 'values' in payload &&
|
|
1368
|
-
name(_getWatch(undefined, defaultValue), payload),
|
|
1369
|
-
})
|
|
1370
|
-
: _getWatch(name, defaultValue, true);
|
|
1609
|
+
const watch = (name, defaultValue) => _getWatch(name, defaultValue, true);
|
|
1371
1610
|
const _subscribe = (props) => _subjects.state.subscribe({
|
|
1372
1611
|
next: (formState) => {
|
|
1373
1612
|
if (shouldSubscribeByName(props.name, formState.name, props.exact) &&
|
|
1374
1613
|
shouldRenderFormState(formState, props.formState || _proxyFormState, _setFormState, props.reRenderRoot)) {
|
|
1614
|
+
const snapshot = { ..._formValues };
|
|
1375
1615
|
props.callback({
|
|
1376
|
-
values:
|
|
1616
|
+
values: snapshot,
|
|
1377
1617
|
..._formState,
|
|
1378
1618
|
...formState,
|
|
1379
1619
|
defaultValues: _defaultValues,
|
|
@@ -1389,7 +1629,10 @@ function createFormControl(props = {}) {
|
|
|
1389
1629
|
};
|
|
1390
1630
|
return _subscribe({
|
|
1391
1631
|
...props,
|
|
1392
|
-
formState:
|
|
1632
|
+
formState: {
|
|
1633
|
+
...defaultProxyFormState,
|
|
1634
|
+
...props.formState,
|
|
1635
|
+
},
|
|
1393
1636
|
});
|
|
1394
1637
|
};
|
|
1395
1638
|
const unregister = (name, options = {}) => {
|
|
@@ -1422,12 +1665,17 @@ function createFormControl(props = {}) {
|
|
|
1422
1665
|
if ((isBoolean(disabled) && _state.mount) ||
|
|
1423
1666
|
!!disabled ||
|
|
1424
1667
|
_names.disabled.has(name)) {
|
|
1668
|
+
const wasDisabled = _names.disabled.has(name);
|
|
1669
|
+
const isDisabled = !!disabled;
|
|
1670
|
+
const disabledStateChanged = wasDisabled !== isDisabled;
|
|
1425
1671
|
disabled ? _names.disabled.add(name) : _names.disabled.delete(name);
|
|
1672
|
+
disabledStateChanged && _state.mount && !_state.action && _setValid();
|
|
1426
1673
|
}
|
|
1427
1674
|
};
|
|
1428
1675
|
const register = (name, options = {}) => {
|
|
1429
1676
|
let field = get(_fields, name);
|
|
1430
1677
|
const disabledIsDefined = isBoolean(options.disabled) || isBoolean(_options.disabled);
|
|
1678
|
+
const shouldRevalidateRemount = !_names.registerName.has(name) && field && field._f && !field._f.mount;
|
|
1431
1679
|
set(_fields, name, {
|
|
1432
1680
|
...(field || {}),
|
|
1433
1681
|
_f: {
|
|
@@ -1438,7 +1686,7 @@ function createFormControl(props = {}) {
|
|
|
1438
1686
|
},
|
|
1439
1687
|
});
|
|
1440
1688
|
_names.mount.add(name);
|
|
1441
|
-
if (field) {
|
|
1689
|
+
if (field && !shouldRevalidateRemount) {
|
|
1442
1690
|
_setDisabledField({
|
|
1443
1691
|
disabled: isBoolean(options.disabled)
|
|
1444
1692
|
? options.disabled
|
|
@@ -1468,7 +1716,9 @@ function createFormControl(props = {}) {
|
|
|
1468
1716
|
onBlur: onChange,
|
|
1469
1717
|
ref: (ref) => {
|
|
1470
1718
|
if (ref) {
|
|
1719
|
+
_names.registerName.add(name);
|
|
1471
1720
|
register(name, options);
|
|
1721
|
+
_names.registerName.delete(name);
|
|
1472
1722
|
field = get(_fields, name);
|
|
1473
1723
|
const fieldRef = isUndefined(ref.value)
|
|
1474
1724
|
? ref.querySelectorAll
|
|
@@ -1505,13 +1755,15 @@ function createFormControl(props = {}) {
|
|
|
1505
1755
|
field._f.mount = false;
|
|
1506
1756
|
}
|
|
1507
1757
|
(_options.shouldUnregister || options.shouldUnregister) &&
|
|
1508
|
-
!(
|
|
1758
|
+
!(getFieldArrayParentNames(_names.array, name).length &&
|
|
1759
|
+
_state.action) &&
|
|
1509
1760
|
_names.unMount.add(name);
|
|
1510
1761
|
}
|
|
1511
1762
|
},
|
|
1512
1763
|
};
|
|
1513
1764
|
};
|
|
1514
1765
|
const _focusError = () => _options.shouldFocusError &&
|
|
1766
|
+
!_options.shouldUseNativeValidation &&
|
|
1515
1767
|
iterateFieldsByAction(_fields, _focusInput, _names.mount);
|
|
1516
1768
|
const _disableForm = (disabled) => {
|
|
1517
1769
|
if (isBoolean(disabled)) {
|
|
@@ -1542,18 +1794,22 @@ function createFormControl(props = {}) {
|
|
|
1542
1794
|
});
|
|
1543
1795
|
if (_options.resolver) {
|
|
1544
1796
|
const { errors, values } = await _runSchema();
|
|
1797
|
+
_updateIsValidating();
|
|
1545
1798
|
_formState.errors = errors;
|
|
1546
1799
|
fieldValues = cloneObject(values);
|
|
1547
1800
|
}
|
|
1548
1801
|
else {
|
|
1549
|
-
await executeBuiltInValidation(
|
|
1802
|
+
await executeBuiltInValidation({
|
|
1803
|
+
fields: _fields,
|
|
1804
|
+
eventType: EVENTS.SUBMIT,
|
|
1805
|
+
});
|
|
1550
1806
|
}
|
|
1551
1807
|
if (_names.disabled.size) {
|
|
1552
1808
|
for (const name of _names.disabled) {
|
|
1553
1809
|
unset(fieldValues, name);
|
|
1554
1810
|
}
|
|
1555
1811
|
}
|
|
1556
|
-
unset(_formState.errors,
|
|
1812
|
+
unset(_formState.errors, ROOT_ERROR_TYPE);
|
|
1557
1813
|
if (isEmptyObject(_formState.errors)) {
|
|
1558
1814
|
_subjects.state.next({
|
|
1559
1815
|
errors: {},
|
|
@@ -1623,9 +1879,15 @@ function createFormControl(props = {}) {
|
|
|
1623
1879
|
...Object.keys(getDirtyFields(_defaultValues, _formValues)),
|
|
1624
1880
|
]);
|
|
1625
1881
|
for (const fieldName of Array.from(fieldsToCheck)) {
|
|
1626
|
-
get(_formState.dirtyFields, fieldName)
|
|
1627
|
-
|
|
1628
|
-
|
|
1882
|
+
const isDirty = get(_formState.dirtyFields, fieldName);
|
|
1883
|
+
const existingValue = get(_formValues, fieldName);
|
|
1884
|
+
const newValue = get(values, fieldName);
|
|
1885
|
+
if (isDirty && !isUndefined(existingValue)) {
|
|
1886
|
+
set(values, fieldName, existingValue);
|
|
1887
|
+
}
|
|
1888
|
+
else if (!isDirty && !isUndefined(newValue)) {
|
|
1889
|
+
setValue(fieldName, newValue);
|
|
1890
|
+
}
|
|
1629
1891
|
}
|
|
1630
1892
|
}
|
|
1631
1893
|
else {
|
|
@@ -1671,6 +1933,7 @@ function createFormControl(props = {}) {
|
|
|
1671
1933
|
mount: keepStateOptions.keepDirtyValues ? _names.mount : new Set(),
|
|
1672
1934
|
unMount: new Set(),
|
|
1673
1935
|
array: new Set(),
|
|
1936
|
+
registerName: new Set(),
|
|
1674
1937
|
disabled: new Set(),
|
|
1675
1938
|
watch: new Set(),
|
|
1676
1939
|
watchAll: false,
|
|
@@ -1682,6 +1945,7 @@ function createFormControl(props = {}) {
|
|
|
1682
1945
|
!!keepStateOptions.keepDirtyValues ||
|
|
1683
1946
|
(!_options.shouldUnregister && !isEmptyObject(values));
|
|
1684
1947
|
_state.watch = !!_options.shouldUnregister;
|
|
1948
|
+
_state.keepIsValid = !!keepStateOptions.keepIsValid;
|
|
1685
1949
|
_state.action = false;
|
|
1686
1950
|
// Clear errors synchronously to prevent validation errors on subsequent submissions
|
|
1687
1951
|
// This fixes the issue where form.reset() causes validation errors on subsequent
|
|
@@ -1726,7 +1990,7 @@ function createFormControl(props = {}) {
|
|
|
1726
1990
|
};
|
|
1727
1991
|
const reset = (formValues, keepStateOptions) => _reset(isFunction(formValues)
|
|
1728
1992
|
? formValues(_formValues)
|
|
1729
|
-
: formValues, keepStateOptions);
|
|
1993
|
+
: formValues, { ..._options.resetOptions, ...keepStateOptions });
|
|
1730
1994
|
const setFocus = (name, options = {}) => {
|
|
1731
1995
|
const field = get(_fields, name);
|
|
1732
1996
|
const fieldReference = field && field._f;
|
|
@@ -1735,10 +1999,14 @@ function createFormControl(props = {}) {
|
|
|
1735
1999
|
? fieldReference.refs[0]
|
|
1736
2000
|
: fieldReference.ref;
|
|
1737
2001
|
if (fieldRef.focus) {
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
fieldRef.
|
|
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
|
+
setTimeout(() => {
|
|
2005
|
+
fieldRef.focus();
|
|
2006
|
+
options.shouldSelect &&
|
|
2007
|
+
isFunction(fieldRef.select) &&
|
|
2008
|
+
fieldRef.select();
|
|
2009
|
+
});
|
|
1742
2010
|
}
|
|
1743
2011
|
}
|
|
1744
2012
|
};
|
|
@@ -1764,6 +2032,7 @@ function createFormControl(props = {}) {
|
|
|
1764
2032
|
setError,
|
|
1765
2033
|
_subscribe,
|
|
1766
2034
|
_runSchema,
|
|
2035
|
+
_updateIsValidating,
|
|
1767
2036
|
_focusError,
|
|
1768
2037
|
_getWatch,
|
|
1769
2038
|
_getDirty,
|
|
@@ -1818,6 +2087,7 @@ function createFormControl(props = {}) {
|
|
|
1818
2087
|
handleSubmit,
|
|
1819
2088
|
watch,
|
|
1820
2089
|
setValue,
|
|
2090
|
+
setValues,
|
|
1821
2091
|
getValues,
|
|
1822
2092
|
reset,
|
|
1823
2093
|
resetField,
|