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.
Files changed (55) hide show
  1. package/README.md +16 -29
  2. package/dist/constants.d.ts +5 -0
  3. package/dist/constants.d.ts.map +1 -1
  4. package/dist/form.d.ts.map +1 -1
  5. package/dist/index.cjs.js +1 -1
  6. package/dist/index.cjs.js.map +1 -1
  7. package/dist/index.esm.mjs +541 -217
  8. package/dist/index.esm.mjs.map +1 -1
  9. package/dist/index.umd.js +1 -1
  10. package/dist/index.umd.js.map +1 -1
  11. package/dist/logic/createFormControl.d.ts +13 -0
  12. package/dist/logic/createFormControl.d.ts.map +1 -1
  13. package/dist/logic/getDirtyFields.d.ts.map +1 -1
  14. package/dist/logic/{isNameInFieldArray.d.ts → getFieldArrayParentNames.d.ts} +2 -2
  15. package/dist/logic/getFieldArrayParentNames.d.ts.map +1 -0
  16. package/dist/logic/getProxyFormState.d.ts.map +1 -1
  17. package/dist/logic/getResolverOptions.d.ts +2 -2
  18. package/dist/logic/hasValidation.d.ts +1 -1
  19. package/dist/logic/shouldRenderFormState.d.ts.map +1 -1
  20. package/dist/logic/updateFieldArrayRootError.d.ts.map +1 -1
  21. package/dist/logic/validateField.d.ts.map +1 -1
  22. package/dist/react-server.esm.mjs +362 -92
  23. package/dist/react-server.esm.mjs.map +1 -1
  24. package/dist/types/errors.d.ts +1 -0
  25. package/dist/types/errors.d.ts.map +1 -1
  26. package/dist/types/fieldArray.d.ts +1 -2
  27. package/dist/types/fieldArray.d.ts.map +1 -1
  28. package/dist/types/form.d.ts +14 -41
  29. package/dist/types/form.d.ts.map +1 -1
  30. package/dist/types/index.d.ts +1 -0
  31. package/dist/types/index.d.ts.map +1 -1
  32. package/dist/types/path/eager.d.ts +1 -1
  33. package/dist/types/path/eager.d.ts.map +1 -1
  34. package/dist/types/utils.d.ts +5 -4
  35. package/dist/types/utils.d.ts.map +1 -1
  36. package/dist/types/validator.d.ts +13 -1
  37. package/dist/types/validator.d.ts.map +1 -1
  38. package/dist/types/watch.d.ts +32 -0
  39. package/dist/types/watch.d.ts.map +1 -0
  40. package/dist/useController.d.ts.map +1 -1
  41. package/dist/useFieldArray.d.ts +2 -2
  42. package/dist/useFieldArray.d.ts.map +1 -1
  43. package/dist/useForm.d.ts.map +1 -1
  44. package/dist/useFormContext.d.ts.map +1 -1
  45. package/dist/useFormControlContext.d.ts +12 -0
  46. package/dist/useFormControlContext.d.ts.map +1 -0
  47. package/dist/useFormState.d.ts.map +1 -1
  48. package/dist/useWatch.d.ts.map +1 -1
  49. package/dist/utils/deepEqual.d.ts +1 -1
  50. package/dist/utils/deepEqual.d.ts.map +1 -1
  51. package/dist/utils/unset.d.ts.map +1 -1
  52. package/dist/watch.d.ts +7 -17
  53. package/dist/watch.d.ts.map +1 -1
  54. package/package.json +29 -29
  55. 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, _internal_visited = new WeakSet()) {
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() === object2.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 (_internal_visited.has(object1) || _internal_visited.has(object2)) {
126
+ if (visited.has(object1) || visited.has(object2)) {
118
127
  return true;
119
128
  }
120
- _internal_visited.add(object1);
121
- _internal_visited.add(object2);
129
+ visited.add(object1);
130
+ visited.add(object2);
122
131
  for (const key of keys1) {
123
132
  const val1 = object1[key];
124
- if (!keys2.includes(key)) {
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) && isObject(val2)) ||
131
- (Array.isArray(val1) && Array.isArray(val2))
132
- ? !deepEqual(val1, val2, _internal_visited)
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 result = (isKey(path) ? [path] : stringToPath(path)).reduce((result, key) => isNullOrUndefined(result) ? result : result[key], object);
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
- object = isUndefined(object) ? index++ : object[updatePath[index++]];
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
- Object.keys(formState).length >= Object.keys(_proxyFormState).length ||
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, 'root', error[name]);
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
- submitCount: 0,
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 _proxyFormState = {
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
- const isValid = _options.resolver
843
- ? isEmptyObject((await _runSchema()).errors)
844
- : await executeBuiltInValidation(_fields, true);
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
- _formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
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 && _setValid();
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
- ? unset(_formState.dirtyFields, name)
948
- : set(_formState.dirtyFields, name, true);
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
- const result = await _options.resolver(_formValues, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation));
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
- ? set(_formState.errors, name, error)
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 executeBuiltInValidation = async (fields, shouldOnlyCheckValid, context = {
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
- if (isPromiseFunction && _proxyFormState.validatingFields) {
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 && !shouldOnlyCheckValid, isFieldArrayRoot);
1039
- if (isPromiseFunction && _proxyFormState.validatingFields) {
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 (shouldOnlyCheckValid) {
1212
+ if (onlyCheckValid) {
1045
1213
  break;
1046
1214
  }
1047
1215
  }
1048
- !shouldOnlyCheckValid &&
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(fieldValue, shouldOnlyCheckValid, context));
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 setValues = (name, value, options) => {
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
- ? setValues(fieldName, fieldValue, options)
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
- set(_formValues, name, cloneValue);
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: getDirtyFields(_defaultValues, _formValues),
1351
+ dirtyFields: _formState.dirtyFields,
1170
1352
  isDirty: _getDirty(name, cloneValue),
1171
1353
  });
1172
1354
  }
1173
1355
  }
1174
1356
  else {
1175
- field && !field._f && !isNullOrUndefined(cloneValue)
1176
- ? setValues(name, cloneValue, options)
1177
- : setFieldValue(name, cloneValue, options);
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(_fields, true);
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(field && field._f ? { [fieldName]: field } : field);
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(_fields);
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
- convertToArrayPayload(name).forEach((inputName) => unset(_formState.errors, inputName));
1344
- _subjects.state.next({
1345
- errors: name ? _formState.errors : {},
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) => isFunction(name)
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: { ..._formValues },
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: _proxySubscribeFormState,
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
- !(isNameInFieldArray(_names.array, name) && _state.action) &&
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(_fields);
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, 'root');
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
- ? set(values, fieldName, get(_formValues, fieldName))
1628
- : setValue(fieldName, get(values, fieldName));
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
- fieldRef.focus();
1739
- options.shouldSelect &&
1740
- isFunction(fieldRef.select) &&
1741
- fieldRef.select();
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,