react-hook-form 7.54.1 → 7.55.0-next.0

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.
@@ -217,43 +217,6 @@ var getProxyFormState = (formState, control, localProxyFormState, isRoot = true)
217
217
  return result;
218
218
  };
219
219
 
220
- var isEmptyObject = (value) => isObject(value) && !Object.keys(value).length;
221
-
222
- var shouldRenderFormState = (formStateData, _proxyFormState, updateFormState, isRoot) => {
223
- updateFormState(formStateData);
224
- const { name, ...formState } = formStateData;
225
- return (isEmptyObject(formState) ||
226
- Object.keys(formState).length >= Object.keys(_proxyFormState).length ||
227
- Object.keys(formState).find((key) => _proxyFormState[key] ===
228
- (!isRoot || VALIDATION_MODE.all)));
229
- };
230
-
231
- var convertToArrayPayload = (value) => (Array.isArray(value) ? value : [value]);
232
-
233
- var shouldSubscribeByName = (name, signalName, exact) => !name ||
234
- !signalName ||
235
- name === signalName ||
236
- convertToArrayPayload(name).some((currentName) => currentName &&
237
- (exact
238
- ? currentName === signalName
239
- : currentName.startsWith(signalName) ||
240
- signalName.startsWith(currentName)));
241
-
242
- function useSubscribe(props) {
243
- const _props = React.useRef(props);
244
- _props.current = props;
245
- React.useEffect(() => {
246
- const subscription = !props.disabled &&
247
- _props.current.subject &&
248
- _props.current.subject.subscribe({
249
- next: _props.current.next,
250
- });
251
- return () => {
252
- subscription && subscription.unsubscribe();
253
- };
254
- }, [props.disabled]);
255
- }
256
-
257
220
  /**
258
221
  * This custom hook allows you to subscribe to each form state, and isolate the re-render at the custom hook level. It has its scope in terms of form state subscription, so it would not affect other useFormState and useForm. Using this hook can reduce the re-render impact on large and complex form application.
259
222
  *
@@ -288,7 +251,6 @@ function useFormState(props) {
288
251
  const methods = useFormContext();
289
252
  const { control = methods.control, disabled, name, exact } = props || {};
290
253
  const [formState, updateFormState] = React.useState(control._formState);
291
- const _mounted = React.useRef(true);
292
254
  const _localProxyFormState = React.useRef({
293
255
  isDirty: false,
294
256
  isLoading: false,
@@ -301,23 +263,20 @@ function useFormState(props) {
301
263
  });
302
264
  const _name = React.useRef(name);
303
265
  _name.current = name;
304
- useSubscribe({
305
- disabled,
306
- next: (value) => _mounted.current &&
307
- shouldSubscribeByName(_name.current, value.name, exact) &&
308
- shouldRenderFormState(value, _localProxyFormState.current, control._updateFormState) &&
309
- updateFormState({
310
- ...control._formState,
311
- ...value,
312
- }),
313
- subject: control._subjects.state,
314
- });
266
+ React.useEffect(() => control._subscribe({
267
+ name: _name.current,
268
+ formState: _localProxyFormState.current,
269
+ exact,
270
+ callback: (formState) => {
271
+ !disabled &&
272
+ updateFormState({
273
+ ...control._formState,
274
+ ...formState,
275
+ });
276
+ },
277
+ }), [control, disabled, exact]);
315
278
  React.useEffect(() => {
316
- _mounted.current = true;
317
- _localProxyFormState.current.isValid && control._updateValid(true);
318
- return () => {
319
- _mounted.current = false;
320
- };
279
+ _localProxyFormState.current.isValid && control._setValid(true);
321
280
  }, [control]);
322
281
  return React.useMemo(() => getProxyFormState(formState, control, _localProxyFormState.current, false), [formState, control]);
323
282
  }
@@ -357,15 +316,15 @@ function useWatch(props) {
357
316
  const { control = methods.control, name, defaultValue, disabled, exact, } = props || {};
358
317
  const _name = React.useRef(name);
359
318
  _name.current = name;
360
- useSubscribe({
361
- disabled,
362
- subject: control._subjects.values,
363
- next: (formState) => {
364
- if (shouldSubscribeByName(_name.current, formState.name, exact)) {
365
- updateValue(cloneObject(generateWatchOutput(_name.current, control._names, formState.values || control._formValues, false, defaultValue)));
366
- }
319
+ React.useEffect(() => control._subscribe({
320
+ name: _name.current,
321
+ formState: {
322
+ values: true,
367
323
  },
368
- });
324
+ exact,
325
+ callback: (formState) => !disabled &&
326
+ updateValue(cloneObject(generateWatchOutput(_name.current, control._names, formState.values || control._formValues, false, defaultValue))),
327
+ }), [control, defaultValue, disabled, exact]);
369
328
  const [value, updateValue] = React.useState(control._getWatch(name, defaultValue));
370
329
  React.useEffect(() => control._removeUnmounted());
371
330
  return value;
@@ -492,6 +451,7 @@ function useController(props) {
492
451
  set(control._formValues, name, value);
493
452
  }
494
453
  }
454
+ !isArrayField && control.register(name);
495
455
  return () => {
496
456
  (isArrayField
497
457
  ? _shouldUnregisterField && !control._state.action
@@ -501,14 +461,11 @@ function useController(props) {
501
461
  };
502
462
  }, [name, control, isArrayField, shouldUnregister]);
503
463
  React.useEffect(() => {
504
- if (isBoolean(disabled) && get(control._fields, name)) {
505
- control._updateDisabledField({
506
- disabled,
507
- fields: control._fields,
508
- name,
509
- value: get(control._fields, name)._f.value,
510
- });
511
- }
464
+ control._setDisabledField({
465
+ disabled,
466
+ fields: control._fields,
467
+ name,
468
+ });
512
469
  }, [disabled, name, control]);
513
470
  return React.useMemo(() => ({
514
471
  field,
@@ -686,67 +643,70 @@ var appendErrors = (name, validateAllFieldCriteria, errors, type, message) => va
686
643
  }
687
644
  : {};
688
645
 
689
- var generateId = () => {
690
- const d = typeof performance === 'undefined' ? Date.now() : performance.now() * 1000;
691
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
692
- const r = (Math.random() * 16 + d) % 16 | 0;
693
- return (c == 'x' ? r : (r & 0x3) | 0x8).toString(16);
694
- });
695
- };
696
-
697
- var getFocusFieldName = (name, index, options = {}) => options.shouldFocus || isUndefined(options.shouldFocus)
698
- ? options.focusName ||
699
- `${name}.${isUndefined(options.focusIndex) ? index : options.focusIndex}.`
700
- : '';
646
+ var convertToArrayPayload = (value) => (Array.isArray(value) ? value : [value]);
701
647
 
702
- var getValidationModes = (mode) => ({
703
- isOnSubmit: !mode || mode === VALIDATION_MODE.onSubmit,
704
- isOnBlur: mode === VALIDATION_MODE.onBlur,
705
- isOnChange: mode === VALIDATION_MODE.onChange,
706
- isOnAll: mode === VALIDATION_MODE.all,
707
- isOnTouch: mode === VALIDATION_MODE.onTouched,
708
- });
648
+ var createSubject = () => {
649
+ let _observers = [];
650
+ const next = (value) => {
651
+ for (const observer of _observers) {
652
+ observer.next && observer.next(value);
653
+ }
654
+ };
655
+ const subscribe = (observer) => {
656
+ _observers.push(observer);
657
+ return {
658
+ unsubscribe: () => {
659
+ _observers = _observers.filter((o) => o !== observer);
660
+ },
661
+ };
662
+ };
663
+ const unsubscribe = () => {
664
+ _observers = [];
665
+ };
666
+ return {
667
+ get observers() {
668
+ return _observers;
669
+ },
670
+ next,
671
+ subscribe,
672
+ unsubscribe,
673
+ };
674
+ };
709
675
 
710
- var isWatched = (name, _names, isBlurEvent) => !isBlurEvent &&
711
- (_names.watchAll ||
712
- _names.watch.has(name) ||
713
- [..._names.watch].some((watchName) => name.startsWith(watchName) &&
714
- /^\.\w+/.test(name.slice(watchName.length))));
676
+ var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
715
677
 
716
- const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
717
- for (const key of fieldsNames || Object.keys(fields)) {
718
- const field = get(fields, key);
719
- if (field) {
720
- const { _f, ...currentField } = field;
721
- if (_f) {
722
- if (_f.refs && _f.refs[0] && action(_f.refs[0], key) && !abortEarly) {
723
- return true;
724
- }
725
- else if (_f.ref && action(_f.ref, _f.name) && !abortEarly) {
726
- return true;
727
- }
728
- else {
729
- if (iterateFieldsByAction(currentField, action)) {
730
- break;
731
- }
732
- }
733
- }
734
- else if (isObject(currentField)) {
735
- if (iterateFieldsByAction(currentField, action)) {
736
- break;
737
- }
678
+ function deepEqual(object1, object2) {
679
+ if (isPrimitive(object1) || isPrimitive(object2)) {
680
+ return object1 === object2;
681
+ }
682
+ if (isDateObject(object1) && isDateObject(object2)) {
683
+ return object1.getTime() === object2.getTime();
684
+ }
685
+ const keys1 = Object.keys(object1);
686
+ const keys2 = Object.keys(object2);
687
+ if (keys1.length !== keys2.length) {
688
+ return false;
689
+ }
690
+ for (const key of keys1) {
691
+ const val1 = object1[key];
692
+ if (!keys2.includes(key)) {
693
+ return false;
694
+ }
695
+ if (key !== 'ref') {
696
+ const val2 = object2[key];
697
+ if ((isDateObject(val1) && isDateObject(val2)) ||
698
+ (isObject(val1) && isObject(val2)) ||
699
+ (Array.isArray(val1) && Array.isArray(val2))
700
+ ? !deepEqual(val1, val2)
701
+ : val1 !== val2) {
702
+ return false;
738
703
  }
739
704
  }
740
705
  }
741
- return;
742
- };
706
+ return true;
707
+ }
743
708
 
744
- var updateFieldArrayRootError = (errors, error, name) => {
745
- const fieldArrayErrors = convertToArrayPayload(get(errors, name));
746
- set(fieldArrayErrors, 'root', error[name]);
747
- set(errors, name, fieldArrayErrors);
748
- return errors;
749
- };
709
+ var isEmptyObject = (value) => isObject(value) && !Object.keys(value).length;
750
710
 
751
711
  var isFileInput = (element) => element.type === 'file';
752
712
 
@@ -761,11 +721,99 @@ var isHTMLElement = (value) => {
761
721
  (owner && owner.defaultView ? owner.defaultView.HTMLElement : HTMLElement));
762
722
  };
763
723
 
764
- var isMessage = (value) => isString(value);
724
+ var isMultipleSelect = (element) => element.type === `select-multiple`;
765
725
 
766
726
  var isRadioInput = (element) => element.type === 'radio';
767
727
 
768
- var isRegex = (value) => value instanceof RegExp;
728
+ var isRadioOrCheckbox = (ref) => isRadioInput(ref) || isCheckBoxInput(ref);
729
+
730
+ var live = (ref) => isHTMLElement(ref) && ref.isConnected;
731
+
732
+ function baseGet(object, updatePath) {
733
+ const length = updatePath.slice(0, -1).length;
734
+ let index = 0;
735
+ while (index < length) {
736
+ object = isUndefined(object) ? index++ : object[updatePath[index++]];
737
+ }
738
+ return object;
739
+ }
740
+ function isEmptyArray(obj) {
741
+ for (const key in obj) {
742
+ if (obj.hasOwnProperty(key) && !isUndefined(obj[key])) {
743
+ return false;
744
+ }
745
+ }
746
+ return true;
747
+ }
748
+ function unset(object, path) {
749
+ const paths = Array.isArray(path)
750
+ ? path
751
+ : isKey(path)
752
+ ? [path]
753
+ : stringToPath(path);
754
+ const childObject = paths.length === 1 ? object : baseGet(object, paths);
755
+ const index = paths.length - 1;
756
+ const key = paths[index];
757
+ if (childObject) {
758
+ delete childObject[key];
759
+ }
760
+ if (index !== 0 &&
761
+ ((isObject(childObject) && isEmptyObject(childObject)) ||
762
+ (Array.isArray(childObject) && isEmptyArray(childObject)))) {
763
+ unset(object, paths.slice(0, -1));
764
+ }
765
+ return object;
766
+ }
767
+
768
+ var objectHasFunction = (data) => {
769
+ for (const key in data) {
770
+ if (isFunction(data[key])) {
771
+ return true;
772
+ }
773
+ }
774
+ return false;
775
+ };
776
+
777
+ function markFieldsDirty(data, fields = {}) {
778
+ const isParentNodeArray = Array.isArray(data);
779
+ if (isObject(data) || isParentNodeArray) {
780
+ for (const key in data) {
781
+ if (Array.isArray(data[key]) ||
782
+ (isObject(data[key]) && !objectHasFunction(data[key]))) {
783
+ fields[key] = Array.isArray(data[key]) ? [] : {};
784
+ markFieldsDirty(data[key], fields[key]);
785
+ }
786
+ else if (!isNullOrUndefined(data[key])) {
787
+ fields[key] = true;
788
+ }
789
+ }
790
+ }
791
+ return fields;
792
+ }
793
+ function getDirtyFieldsFromDefaultValues(data, formValues, dirtyFieldsFromValues) {
794
+ const isParentNodeArray = Array.isArray(data);
795
+ if (isObject(data) || isParentNodeArray) {
796
+ for (const key in data) {
797
+ if (Array.isArray(data[key]) ||
798
+ (isObject(data[key]) && !objectHasFunction(data[key]))) {
799
+ if (isUndefined(formValues) ||
800
+ isPrimitive(dirtyFieldsFromValues[key])) {
801
+ dirtyFieldsFromValues[key] = Array.isArray(data[key])
802
+ ? markFieldsDirty(data[key], [])
803
+ : { ...markFieldsDirty(data[key]) };
804
+ }
805
+ else {
806
+ getDirtyFieldsFromDefaultValues(data[key], isNullOrUndefined(formValues) ? {} : formValues[key], dirtyFieldsFromValues[key]);
807
+ }
808
+ }
809
+ else {
810
+ dirtyFieldsFromValues[key] = !deepEqual(data[key], formValues[key]);
811
+ }
812
+ }
813
+ }
814
+ return dirtyFieldsFromValues;
815
+ }
816
+ var getDirtyFields = (defaultValues, formValues) => getDirtyFieldsFromDefaultValues(defaultValues, formValues, markFieldsDirty(formValues));
769
817
 
770
818
  const defaultResult = {
771
819
  value: false,
@@ -792,19 +840,209 @@ var getCheckboxValue = (options) => {
792
840
  return defaultResult;
793
841
  };
794
842
 
795
- const defaultReturn = {
796
- isValid: false,
797
- value: null,
798
- };
799
- var getRadioValue = (options) => Array.isArray(options)
800
- ? options.reduce((previous, option) => option && option.checked && !option.disabled
801
- ? {
802
- isValid: true,
803
- value: option.value,
843
+ var getFieldValueAs = (value, { valueAsNumber, valueAsDate, setValueAs }) => isUndefined(value)
844
+ ? value
845
+ : valueAsNumber
846
+ ? value === ''
847
+ ? NaN
848
+ : value
849
+ ? +value
850
+ : value
851
+ : valueAsDate && isString(value)
852
+ ? new Date(value)
853
+ : setValueAs
854
+ ? setValueAs(value)
855
+ : value;
856
+
857
+ const defaultReturn = {
858
+ isValid: false,
859
+ value: null,
860
+ };
861
+ var getRadioValue = (options) => Array.isArray(options)
862
+ ? options.reduce((previous, option) => option && option.checked && !option.disabled
863
+ ? {
864
+ isValid: true,
865
+ value: option.value,
804
866
  }
805
867
  : previous, defaultReturn)
806
868
  : defaultReturn;
807
869
 
870
+ function getFieldValue(_f) {
871
+ const ref = _f.ref;
872
+ if (isFileInput(ref)) {
873
+ return ref.files;
874
+ }
875
+ if (isRadioInput(ref)) {
876
+ return getRadioValue(_f.refs).value;
877
+ }
878
+ if (isMultipleSelect(ref)) {
879
+ return [...ref.selectedOptions].map(({ value }) => value);
880
+ }
881
+ if (isCheckBoxInput(ref)) {
882
+ return getCheckboxValue(_f.refs).value;
883
+ }
884
+ return getFieldValueAs(isUndefined(ref.value) ? _f.ref.value : ref.value, _f);
885
+ }
886
+
887
+ var getResolverOptions = (fieldsNames, _fields, criteriaMode, shouldUseNativeValidation) => {
888
+ const fields = {};
889
+ for (const name of fieldsNames) {
890
+ const field = get(_fields, name);
891
+ field && set(fields, name, field._f);
892
+ }
893
+ return {
894
+ criteriaMode,
895
+ names: [...fieldsNames],
896
+ fields,
897
+ shouldUseNativeValidation,
898
+ };
899
+ };
900
+
901
+ var isRegex = (value) => value instanceof RegExp;
902
+
903
+ var getRuleValue = (rule) => isUndefined(rule)
904
+ ? rule
905
+ : isRegex(rule)
906
+ ? rule.source
907
+ : isObject(rule)
908
+ ? isRegex(rule.value)
909
+ ? rule.value.source
910
+ : rule.value
911
+ : rule;
912
+
913
+ var getValidationModes = (mode) => ({
914
+ isOnSubmit: !mode || mode === VALIDATION_MODE.onSubmit,
915
+ isOnBlur: mode === VALIDATION_MODE.onBlur,
916
+ isOnChange: mode === VALIDATION_MODE.onChange,
917
+ isOnAll: mode === VALIDATION_MODE.all,
918
+ isOnTouch: mode === VALIDATION_MODE.onTouched,
919
+ });
920
+
921
+ const ASYNC_FUNCTION = 'AsyncFunction';
922
+ var hasPromiseValidation = (fieldReference) => !!fieldReference &&
923
+ !!fieldReference.validate &&
924
+ !!((isFunction(fieldReference.validate) &&
925
+ fieldReference.validate.constructor.name === ASYNC_FUNCTION) ||
926
+ (isObject(fieldReference.validate) &&
927
+ Object.values(fieldReference.validate).find((validateFunction) => validateFunction.constructor.name === ASYNC_FUNCTION)));
928
+
929
+ var hasValidation = (options) => options.mount &&
930
+ (options.required ||
931
+ options.min ||
932
+ options.max ||
933
+ options.maxLength ||
934
+ options.minLength ||
935
+ options.pattern ||
936
+ options.validate);
937
+
938
+ var isWatched = (name, _names, isBlurEvent) => !isBlurEvent &&
939
+ (_names.watchAll ||
940
+ _names.watch.has(name) ||
941
+ [..._names.watch].some((watchName) => name.startsWith(watchName) &&
942
+ /^\.\w+/.test(name.slice(watchName.length))));
943
+
944
+ const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
945
+ for (const key of fieldsNames || Object.keys(fields)) {
946
+ const field = get(fields, key);
947
+ if (field) {
948
+ const { _f, ...currentField } = field;
949
+ if (_f) {
950
+ if (_f.refs && _f.refs[0] && action(_f.refs[0], key) && !abortEarly) {
951
+ return true;
952
+ }
953
+ else if (_f.ref && action(_f.ref, _f.name) && !abortEarly) {
954
+ return true;
955
+ }
956
+ else {
957
+ if (iterateFieldsByAction(currentField, action)) {
958
+ break;
959
+ }
960
+ }
961
+ }
962
+ else if (isObject(currentField)) {
963
+ if (iterateFieldsByAction(currentField, action)) {
964
+ break;
965
+ }
966
+ }
967
+ }
968
+ }
969
+ return;
970
+ };
971
+
972
+ function schemaErrorLookup(errors, _fields, name) {
973
+ const error = get(errors, name);
974
+ if (error || isKey(name)) {
975
+ return {
976
+ error,
977
+ name,
978
+ };
979
+ }
980
+ const names = name.split('.');
981
+ while (names.length) {
982
+ const fieldName = names.join('.');
983
+ const field = get(_fields, fieldName);
984
+ const foundError = get(errors, fieldName);
985
+ if (field && !Array.isArray(field) && name !== fieldName) {
986
+ return { name };
987
+ }
988
+ if (foundError && foundError.type) {
989
+ return {
990
+ name: fieldName,
991
+ error: foundError,
992
+ };
993
+ }
994
+ names.pop();
995
+ }
996
+ return {
997
+ name,
998
+ };
999
+ }
1000
+
1001
+ var shouldRenderFormState = (formStateData, _proxyFormState, updateFormState, isRoot) => {
1002
+ updateFormState(formStateData);
1003
+ const { name, ...formState } = formStateData;
1004
+ return (isEmptyObject(formState) ||
1005
+ Object.keys(formState).length >= Object.keys(_proxyFormState).length ||
1006
+ Object.keys(formState).find((key) => _proxyFormState[key] ===
1007
+ (!isRoot || VALIDATION_MODE.all)));
1008
+ };
1009
+
1010
+ var shouldSubscribeByName = (name, signalName, exact) => !name ||
1011
+ !signalName ||
1012
+ name === signalName ||
1013
+ convertToArrayPayload(name).some((currentName) => currentName &&
1014
+ (exact
1015
+ ? currentName === signalName
1016
+ : currentName.startsWith(signalName) ||
1017
+ signalName.startsWith(currentName)));
1018
+
1019
+ var skipValidation = (isBlurEvent, isTouched, isSubmitted, reValidateMode, mode) => {
1020
+ if (mode.isOnAll) {
1021
+ return false;
1022
+ }
1023
+ else if (!isSubmitted && mode.isOnTouch) {
1024
+ return !(isTouched || isBlurEvent);
1025
+ }
1026
+ else if (isSubmitted ? reValidateMode.isOnBlur : mode.isOnBlur) {
1027
+ return !isBlurEvent;
1028
+ }
1029
+ else if (isSubmitted ? reValidateMode.isOnChange : mode.isOnChange) {
1030
+ return isBlurEvent;
1031
+ }
1032
+ return true;
1033
+ };
1034
+
1035
+ var unsetEmptyArray = (ref, name) => !compact(get(ref, name)).length && unset(ref, name);
1036
+
1037
+ var updateFieldArrayRootError = (errors, error, name) => {
1038
+ const fieldArrayErrors = convertToArrayPayload(get(errors, name));
1039
+ set(fieldArrayErrors, 'root', error[name]);
1040
+ set(errors, name, fieldArrayErrors);
1041
+ return errors;
1042
+ };
1043
+
1044
+ var isMessage = (value) => isString(value);
1045
+
808
1046
  function getValidateError(result, ref, type = 'validate') {
809
1047
  if (isMessage(result) ||
810
1048
  (Array.isArray(result) && result.every(isMessage)) ||
@@ -824,10 +1062,10 @@ var getValueAndMessage = (validationData) => isObject(validationData) && !isRege
824
1062
  message: '',
825
1063
  };
826
1064
 
827
- var validateField = async (field, formValues, validateAllFieldCriteria, shouldUseNativeValidation, isFieldArray) => {
828
- const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, disabled, } = field._f;
1065
+ var validateField = async (field, disabledFieldNames, formValues, validateAllFieldCriteria, shouldUseNativeValidation, isFieldArray) => {
1066
+ const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, } = field._f;
829
1067
  const inputValue = get(formValues, name);
830
- if (!mount || disabled) {
1068
+ if (!mount || disabledFieldNames.has(name)) {
831
1069
  return {};
832
1070
  }
833
1071
  const inputRef = refs ? refs[0] : ref;
@@ -1003,1565 +1241,1337 @@ var validateField = async (field, formValues, validateAllFieldCriteria, shouldUs
1003
1241
  return error;
1004
1242
  };
1005
1243
 
1006
- var appendAt = (data, value) => [
1007
- ...data,
1008
- ...convertToArrayPayload(value),
1009
- ];
1010
-
1011
- var fillEmptyArray = (value) => Array.isArray(value) ? value.map(() => undefined) : undefined;
1012
-
1013
- function insert(data, index, value) {
1014
- return [
1015
- ...data.slice(0, index),
1016
- ...convertToArrayPayload(value),
1017
- ...data.slice(index),
1018
- ];
1019
- }
1020
-
1021
- var moveArrayAt = (data, from, to) => {
1022
- if (!Array.isArray(data)) {
1023
- return [];
1024
- }
1025
- if (isUndefined(data[to])) {
1026
- data[to] = undefined;
1027
- }
1028
- data.splice(to, 0, data.splice(from, 1)[0]);
1029
- return data;
1030
- };
1031
-
1032
- var prependAt = (data, value) => [
1033
- ...convertToArrayPayload(value),
1034
- ...convertToArrayPayload(data),
1035
- ];
1036
-
1037
- function removeAtIndexes(data, indexes) {
1038
- let i = 0;
1039
- const temp = [...data];
1040
- for (const index of indexes) {
1041
- temp.splice(index - i, 1);
1042
- i++;
1043
- }
1044
- return compact(temp).length ? temp : [];
1045
- }
1046
- var removeArrayAt = (data, index) => isUndefined(index)
1047
- ? []
1048
- : removeAtIndexes(data, convertToArrayPayload(index).sort((a, b) => a - b));
1049
-
1050
- var swapArrayAt = (data, indexA, indexB) => {
1051
- [data[indexA], data[indexB]] = [data[indexB], data[indexA]];
1244
+ const defaultOptions = {
1245
+ mode: VALIDATION_MODE.onSubmit,
1246
+ reValidateMode: VALIDATION_MODE.onChange,
1247
+ shouldFocusError: true,
1052
1248
  };
1053
-
1054
- function baseGet(object, updatePath) {
1055
- const length = updatePath.slice(0, -1).length;
1056
- let index = 0;
1057
- while (index < length) {
1058
- object = isUndefined(object) ? index++ : object[updatePath[index++]];
1059
- }
1060
- return object;
1061
- }
1062
- function isEmptyArray(obj) {
1063
- for (const key in obj) {
1064
- if (obj.hasOwnProperty(key) && !isUndefined(obj[key])) {
1065
- return false;
1066
- }
1067
- }
1068
- return true;
1069
- }
1070
- function unset(object, path) {
1071
- const paths = Array.isArray(path)
1072
- ? path
1073
- : isKey(path)
1074
- ? [path]
1075
- : stringToPath(path);
1076
- const childObject = paths.length === 1 ? object : baseGet(object, paths);
1077
- const index = paths.length - 1;
1078
- const key = paths[index];
1079
- if (childObject) {
1080
- delete childObject[key];
1081
- }
1082
- if (index !== 0 &&
1083
- ((isObject(childObject) && isEmptyObject(childObject)) ||
1084
- (Array.isArray(childObject) && isEmptyArray(childObject)))) {
1085
- unset(object, paths.slice(0, -1));
1086
- }
1087
- return object;
1088
- }
1089
-
1090
- var updateAt = (fieldValues, index, value) => {
1091
- fieldValues[index] = value;
1092
- return fieldValues;
1093
- };
1094
-
1095
- /**
1096
- * A custom hook that exposes convenient methods to perform operations with a list of dynamic inputs that need to be appended, updated, removed etc. • [Demo](https://codesandbox.io/s/react-hook-form-usefieldarray-ssugn) • [Video](https://youtu.be/4MrbfGSFY2A)
1097
- *
1098
- * @remarks
1099
- * [API](https://react-hook-form.com/docs/usefieldarray) • [Demo](https://codesandbox.io/s/react-hook-form-usefieldarray-ssugn)
1100
- *
1101
- * @param props - useFieldArray props
1102
- *
1103
- * @returns methods - functions to manipulate with the Field Arrays (dynamic inputs) {@link UseFieldArrayReturn}
1104
- *
1105
- * @example
1106
- * ```tsx
1107
- * function App() {
1108
- * const { register, control, handleSubmit, reset, trigger, setError } = useForm({
1109
- * defaultValues: {
1110
- * test: []
1111
- * }
1112
- * });
1113
- * const { fields, append } = useFieldArray({
1114
- * control,
1115
- * name: "test"
1116
- * });
1117
- *
1118
- * return (
1119
- * <form onSubmit={handleSubmit(data => console.log(data))}>
1120
- * {fields.map((item, index) => (
1121
- * <input key={item.id} {...register(`test.${index}.firstName`)} />
1122
- * ))}
1123
- * <button type="button" onClick={() => append({ firstName: "bill" })}>
1124
- * append
1125
- * </button>
1126
- * <input type="submit" />
1127
- * </form>
1128
- * );
1129
- * }
1130
- * ```
1131
- */
1132
- function useFieldArray(props) {
1133
- const methods = useFormContext();
1134
- const { control = methods.control, name, keyName = 'id', shouldUnregister, rules, } = props;
1135
- const [fields, setFields] = React.useState(control._getFieldArray(name));
1136
- const ids = React.useRef(control._getFieldArray(name).map(generateId));
1137
- const _fieldIds = React.useRef(fields);
1138
- const _name = React.useRef(name);
1139
- const _actioned = React.useRef(false);
1140
- _name.current = name;
1141
- _fieldIds.current = fields;
1142
- control._names.array.add(name);
1143
- rules &&
1144
- control.register(name, rules);
1145
- useSubscribe({
1146
- next: ({ values, name: fieldArrayName, }) => {
1147
- if (fieldArrayName === _name.current || !fieldArrayName) {
1148
- const fieldValues = get(values, _name.current);
1149
- if (Array.isArray(fieldValues)) {
1150
- setFields(fieldValues);
1151
- ids.current = fieldValues.map(generateId);
1152
- }
1153
- }
1154
- },
1155
- subject: control._subjects.array,
1156
- });
1157
- const updateValues = React.useCallback((updatedFieldArrayValues) => {
1158
- _actioned.current = true;
1159
- control._updateFieldArray(name, updatedFieldArrayValues);
1160
- }, [control, name]);
1161
- const append = (value, options) => {
1162
- const appendValue = convertToArrayPayload(cloneObject(value));
1163
- const updatedFieldArrayValues = appendAt(control._getFieldArray(name), appendValue);
1164
- control._names.focus = getFocusFieldName(name, updatedFieldArrayValues.length - 1, options);
1165
- ids.current = appendAt(ids.current, appendValue.map(generateId));
1166
- updateValues(updatedFieldArrayValues);
1167
- setFields(updatedFieldArrayValues);
1168
- control._updateFieldArray(name, updatedFieldArrayValues, appendAt, {
1169
- argA: fillEmptyArray(value),
1170
- });
1249
+ function createFormControl(props = {}) {
1250
+ let _options = {
1251
+ ...defaultOptions,
1252
+ ...props,
1171
1253
  };
1172
- const prepend = (value, options) => {
1173
- const prependValue = convertToArrayPayload(cloneObject(value));
1174
- const updatedFieldArrayValues = prependAt(control._getFieldArray(name), prependValue);
1175
- control._names.focus = getFocusFieldName(name, 0, options);
1176
- ids.current = prependAt(ids.current, prependValue.map(generateId));
1177
- updateValues(updatedFieldArrayValues);
1178
- setFields(updatedFieldArrayValues);
1179
- control._updateFieldArray(name, updatedFieldArrayValues, prependAt, {
1180
- argA: fillEmptyArray(value),
1181
- });
1254
+ let _formState = {
1255
+ submitCount: 0,
1256
+ isDirty: false,
1257
+ isLoading: isFunction(_options.defaultValues),
1258
+ isValidating: false,
1259
+ isSubmitted: false,
1260
+ isSubmitting: false,
1261
+ isSubmitSuccessful: false,
1262
+ isValid: false,
1263
+ touchedFields: {},
1264
+ dirtyFields: {},
1265
+ validatingFields: {},
1266
+ errors: _options.errors || {},
1267
+ disabled: _options.disabled || false,
1182
1268
  };
1183
- const remove = (index) => {
1184
- const updatedFieldArrayValues = removeArrayAt(control._getFieldArray(name), index);
1185
- ids.current = removeArrayAt(ids.current, index);
1186
- updateValues(updatedFieldArrayValues);
1187
- setFields(updatedFieldArrayValues);
1188
- !Array.isArray(get(control._fields, name)) &&
1189
- set(control._fields, name, undefined);
1190
- control._updateFieldArray(name, updatedFieldArrayValues, removeArrayAt, {
1191
- argA: index,
1192
- });
1269
+ let _fields = {};
1270
+ let _defaultValues = isObject(_options.defaultValues) || isObject(_options.values)
1271
+ ? cloneObject(_options.defaultValues || _options.values) || {}
1272
+ : {};
1273
+ let _formValues = _options.shouldUnregister
1274
+ ? {}
1275
+ : cloneObject(_defaultValues);
1276
+ let _state = {
1277
+ action: false,
1278
+ mount: false,
1279
+ watch: false,
1193
1280
  };
1194
- const insert$1 = (index, value, options) => {
1195
- const insertValue = convertToArrayPayload(cloneObject(value));
1196
- const updatedFieldArrayValues = insert(control._getFieldArray(name), index, insertValue);
1197
- control._names.focus = getFocusFieldName(name, index, options);
1198
- ids.current = insert(ids.current, index, insertValue.map(generateId));
1199
- updateValues(updatedFieldArrayValues);
1200
- setFields(updatedFieldArrayValues);
1201
- control._updateFieldArray(name, updatedFieldArrayValues, insert, {
1202
- argA: index,
1203
- argB: fillEmptyArray(value),
1204
- });
1281
+ let _names = {
1282
+ mount: new Set(),
1283
+ disabled: new Set(),
1284
+ unMount: new Set(),
1285
+ array: new Set(),
1286
+ watch: new Set(),
1205
1287
  };
1206
- const swap = (indexA, indexB) => {
1207
- const updatedFieldArrayValues = control._getFieldArray(name);
1208
- swapArrayAt(updatedFieldArrayValues, indexA, indexB);
1209
- swapArrayAt(ids.current, indexA, indexB);
1210
- updateValues(updatedFieldArrayValues);
1211
- setFields(updatedFieldArrayValues);
1212
- control._updateFieldArray(name, updatedFieldArrayValues, swapArrayAt, {
1213
- argA: indexA,
1214
- argB: indexB,
1215
- }, false);
1288
+ let delayErrorCallback;
1289
+ let timer = 0;
1290
+ const _proxyFormState = {
1291
+ isDirty: false,
1292
+ dirtyFields: false,
1293
+ validatingFields: false,
1294
+ touchedFields: false,
1295
+ isValidating: false,
1296
+ isValid: false,
1297
+ errors: false,
1216
1298
  };
1217
- const move = (from, to) => {
1218
- const updatedFieldArrayValues = control._getFieldArray(name);
1219
- moveArrayAt(updatedFieldArrayValues, from, to);
1220
- moveArrayAt(ids.current, from, to);
1221
- updateValues(updatedFieldArrayValues);
1222
- setFields(updatedFieldArrayValues);
1223
- control._updateFieldArray(name, updatedFieldArrayValues, moveArrayAt, {
1224
- argA: from,
1225
- argB: to,
1226
- }, false);
1299
+ let _proxySubscribeFormState = {
1300
+ ..._proxyFormState,
1227
1301
  };
1228
- const update = (index, value) => {
1229
- const updateValue = cloneObject(value);
1230
- const updatedFieldArrayValues = updateAt(control._getFieldArray(name), index, updateValue);
1231
- ids.current = [...updatedFieldArrayValues].map((item, i) => !item || i === index ? generateId() : ids.current[i]);
1232
- updateValues(updatedFieldArrayValues);
1233
- setFields([...updatedFieldArrayValues]);
1234
- control._updateFieldArray(name, updatedFieldArrayValues, updateAt, {
1235
- argA: index,
1236
- argB: updateValue,
1237
- }, true, false);
1302
+ const _subjects = {
1303
+ array: createSubject(),
1304
+ state: createSubject(),
1238
1305
  };
1239
- const replace = (value) => {
1240
- const updatedFieldArrayValues = convertToArrayPayload(cloneObject(value));
1241
- ids.current = updatedFieldArrayValues.map(generateId);
1242
- updateValues([...updatedFieldArrayValues]);
1243
- setFields([...updatedFieldArrayValues]);
1244
- control._updateFieldArray(name, [...updatedFieldArrayValues], (data) => data, {}, true, false);
1306
+ const validationModeBeforeSubmit = getValidationModes(_options.mode);
1307
+ const validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
1308
+ const shouldDisplayAllAssociatedErrors = _options.criteriaMode === VALIDATION_MODE.all;
1309
+ const debounce = (callback) => (wait) => {
1310
+ clearTimeout(timer);
1311
+ timer = setTimeout(callback, wait);
1245
1312
  };
1246
- React.useEffect(() => {
1247
- control._state.action = false;
1248
- isWatched(name, control._names) &&
1249
- control._subjects.state.next({
1250
- ...control._formState,
1251
- });
1252
- if (_actioned.current &&
1253
- (!getValidationModes(control._options.mode).isOnSubmit ||
1254
- control._formState.isSubmitted)) {
1255
- if (control._options.resolver) {
1256
- control._executeSchema([name]).then((result) => {
1257
- const error = get(result.errors, name);
1258
- const existingError = get(control._formState.errors, name);
1259
- if (existingError
1260
- ? (!error && existingError.type) ||
1261
- (error &&
1262
- (existingError.type !== error.type ||
1263
- existingError.message !== error.message))
1264
- : error && error.type) {
1265
- error
1266
- ? set(control._formState.errors, name, error)
1267
- : unset(control._formState.errors, name);
1268
- control._subjects.state.next({
1269
- errors: control._formState.errors,
1270
- });
1271
- }
1313
+ const _setValid = async (shouldUpdateValid) => {
1314
+ if (!_options.disabled &&
1315
+ (_proxyFormState.isValid ||
1316
+ _proxySubscribeFormState.isValid ||
1317
+ shouldUpdateValid)) {
1318
+ const isValid = _options.resolver
1319
+ ? isEmptyObject((await _runSchema()).errors)
1320
+ : await executeBuiltInValidation(_fields, true);
1321
+ if (isValid !== _formState.isValid) {
1322
+ _subjects.state.next({
1323
+ isValid,
1272
1324
  });
1273
1325
  }
1274
- else {
1275
- const field = get(control._fields, name);
1276
- if (field &&
1277
- field._f &&
1278
- !(getValidationModes(control._options.reValidateMode).isOnSubmit &&
1279
- getValidationModes(control._options.mode).isOnSubmit)) {
1280
- validateField(field, control._formValues, control._options.criteriaMode === VALIDATION_MODE.all, control._options.shouldUseNativeValidation, true).then((error) => !isEmptyObject(error) &&
1281
- control._subjects.state.next({
1282
- errors: updateFieldArrayRootError(control._formState.errors, error, name),
1283
- }));
1326
+ }
1327
+ };
1328
+ const _updateIsValidating = (names, isValidating) => {
1329
+ if (!_options.disabled &&
1330
+ (_proxyFormState.isValidating ||
1331
+ _proxyFormState.validatingFields ||
1332
+ _proxySubscribeFormState.isValidating ||
1333
+ _proxySubscribeFormState.validatingFields)) {
1334
+ (names || Array.from(_names.mount)).forEach((name) => {
1335
+ if (name) {
1336
+ isValidating
1337
+ ? set(_formState.validatingFields, name, isValidating)
1338
+ : unset(_formState.validatingFields, name);
1284
1339
  }
1340
+ });
1341
+ _subjects.state.next({
1342
+ validatingFields: _formState.validatingFields,
1343
+ isValidating: !isEmptyObject(_formState.validatingFields),
1344
+ });
1345
+ }
1346
+ };
1347
+ const _setFieldArray = (name, values = [], method, args, shouldSetValues = true, shouldUpdateFieldsAndState = true) => {
1348
+ if (args && method && !_options.disabled) {
1349
+ _state.action = true;
1350
+ if (shouldUpdateFieldsAndState && Array.isArray(get(_fields, name))) {
1351
+ const fieldValues = method(get(_fields, name), args.argA, args.argB);
1352
+ shouldSetValues && set(_fields, name, fieldValues);
1353
+ }
1354
+ if (shouldUpdateFieldsAndState &&
1355
+ Array.isArray(get(_formState.errors, name))) {
1356
+ const errors = method(get(_formState.errors, name), args.argA, args.argB);
1357
+ shouldSetValues && set(_formState.errors, name, errors);
1358
+ unsetEmptyArray(_formState.errors, name);
1359
+ }
1360
+ if ((_proxyFormState.touchedFields ||
1361
+ _proxySubscribeFormState.touchedFields) &&
1362
+ shouldUpdateFieldsAndState &&
1363
+ Array.isArray(get(_formState.touchedFields, name))) {
1364
+ const touchedFields = method(get(_formState.touchedFields, name), args.argA, args.argB);
1365
+ shouldSetValues && set(_formState.touchedFields, name, touchedFields);
1285
1366
  }
1367
+ if (_proxyFormState.dirtyFields || _proxySubscribeFormState.dirtyFields) {
1368
+ _formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
1369
+ }
1370
+ _subjects.state.next({
1371
+ name,
1372
+ isDirty: _getDirty(name, values),
1373
+ dirtyFields: _formState.dirtyFields,
1374
+ errors: _formState.errors,
1375
+ isValid: _formState.isValid,
1376
+ });
1286
1377
  }
1287
- control._subjects.values.next({
1288
- name,
1289
- values: { ...control._formValues },
1378
+ else {
1379
+ set(_formValues, name, values);
1380
+ }
1381
+ };
1382
+ const updateErrors = (name, error) => {
1383
+ set(_formState.errors, name, error);
1384
+ _subjects.state.next({
1385
+ errors: _formState.errors,
1290
1386
  });
1291
- control._names.focus &&
1292
- iterateFieldsByAction(control._fields, (ref, key) => {
1293
- if (control._names.focus &&
1294
- key.startsWith(control._names.focus) &&
1295
- ref.focus) {
1296
- ref.focus();
1297
- return 1;
1298
- }
1299
- return;
1300
- });
1301
- control._names.focus = '';
1302
- control._updateValid();
1303
- _actioned.current = false;
1304
- }, [fields, name, control]);
1305
- React.useEffect(() => {
1306
- !get(control._formValues, name) && control._updateFieldArray(name);
1307
- return () => {
1308
- (control._options.shouldUnregister || shouldUnregister) &&
1309
- control.unregister(name);
1310
- };
1311
- }, [name, control, keyName, shouldUnregister]);
1312
- return {
1313
- swap: React.useCallback(swap, [updateValues, name, control]),
1314
- move: React.useCallback(move, [updateValues, name, control]),
1315
- prepend: React.useCallback(prepend, [updateValues, name, control]),
1316
- append: React.useCallback(append, [updateValues, name, control]),
1317
- remove: React.useCallback(remove, [updateValues, name, control]),
1318
- insert: React.useCallback(insert$1, [updateValues, name, control]),
1319
- update: React.useCallback(update, [updateValues, name, control]),
1320
- replace: React.useCallback(replace, [updateValues, name, control]),
1321
- fields: React.useMemo(() => fields.map((field, index) => ({
1322
- ...field,
1323
- [keyName]: ids.current[index] || generateId(),
1324
- })), [fields, keyName]),
1325
1387
  };
1326
- }
1327
-
1328
- var createSubject = () => {
1329
- let _observers = [];
1330
- const next = (value) => {
1331
- for (const observer of _observers) {
1332
- observer.next && observer.next(value);
1388
+ const _setErrors = (errors) => {
1389
+ _formState.errors = errors;
1390
+ _subjects.state.next({
1391
+ errors: _formState.errors,
1392
+ isValid: false,
1393
+ });
1394
+ };
1395
+ const updateValidAndValue = (name, shouldSkipSetValueAs, value, ref) => {
1396
+ const field = get(_fields, name);
1397
+ if (field) {
1398
+ const defaultValue = get(_formValues, name, isUndefined(value) ? get(_defaultValues, name) : value);
1399
+ isUndefined(defaultValue) ||
1400
+ (ref && ref.defaultChecked) ||
1401
+ shouldSkipSetValueAs
1402
+ ? set(_formValues, name, shouldSkipSetValueAs ? defaultValue : getFieldValue(field._f))
1403
+ : setFieldValue(name, defaultValue);
1404
+ _state.mount && _setValid();
1333
1405
  }
1334
1406
  };
1335
- const subscribe = (observer) => {
1336
- _observers.push(observer);
1337
- return {
1338
- unsubscribe: () => {
1339
- _observers = _observers.filter((o) => o !== observer);
1340
- },
1407
+ const updateTouchAndDirty = (name, fieldValue, isBlurEvent, shouldDirty, shouldRender) => {
1408
+ let shouldUpdateField = false;
1409
+ let isPreviousDirty = false;
1410
+ const output = {
1411
+ name,
1341
1412
  };
1413
+ if (!_options.disabled) {
1414
+ const disabledField = !!(get(_fields, name) &&
1415
+ get(_fields, name)._f &&
1416
+ get(_fields, name)._f.disabled);
1417
+ if (!isBlurEvent || shouldDirty) {
1418
+ if (_proxyFormState.isDirty || _proxySubscribeFormState.isDirty) {
1419
+ isPreviousDirty = _formState.isDirty;
1420
+ _formState.isDirty = output.isDirty = _getDirty();
1421
+ shouldUpdateField = isPreviousDirty !== output.isDirty;
1422
+ }
1423
+ const isCurrentFieldPristine = disabledField || deepEqual(get(_defaultValues, name), fieldValue);
1424
+ isPreviousDirty = !!(!disabledField && get(_formState.dirtyFields, name));
1425
+ isCurrentFieldPristine || disabledField
1426
+ ? unset(_formState.dirtyFields, name)
1427
+ : set(_formState.dirtyFields, name, true);
1428
+ output.dirtyFields = _formState.dirtyFields;
1429
+ shouldUpdateField =
1430
+ shouldUpdateField ||
1431
+ ((_proxyFormState.dirtyFields ||
1432
+ _proxySubscribeFormState.dirtyFields) &&
1433
+ isPreviousDirty !== !isCurrentFieldPristine);
1434
+ }
1435
+ if (isBlurEvent) {
1436
+ const isPreviousFieldTouched = get(_formState.touchedFields, name);
1437
+ if (!isPreviousFieldTouched) {
1438
+ set(_formState.touchedFields, name, isBlurEvent);
1439
+ output.touchedFields = _formState.touchedFields;
1440
+ shouldUpdateField =
1441
+ shouldUpdateField ||
1442
+ ((_proxyFormState.touchedFields ||
1443
+ _proxySubscribeFormState.touchedFields) &&
1444
+ isPreviousFieldTouched !== isBlurEvent);
1445
+ }
1446
+ }
1447
+ shouldUpdateField && shouldRender && _subjects.state.next(output);
1448
+ }
1449
+ return shouldUpdateField ? output : {};
1342
1450
  };
1343
- const unsubscribe = () => {
1344
- _observers = [];
1451
+ const shouldRenderByError = (name, isValid, error, fieldState) => {
1452
+ const previousFieldError = get(_formState.errors, name);
1453
+ const shouldUpdateValid = (_proxyFormState.isValid || _proxySubscribeFormState.isValid) &&
1454
+ isBoolean(isValid) &&
1455
+ _formState.isValid !== isValid;
1456
+ if (_options.delayError && error) {
1457
+ delayErrorCallback = debounce(() => updateErrors(name, error));
1458
+ delayErrorCallback(_options.delayError);
1459
+ }
1460
+ else {
1461
+ clearTimeout(timer);
1462
+ delayErrorCallback = null;
1463
+ error
1464
+ ? set(_formState.errors, name, error)
1465
+ : unset(_formState.errors, name);
1466
+ }
1467
+ if ((error ? !deepEqual(previousFieldError, error) : previousFieldError) ||
1468
+ !isEmptyObject(fieldState) ||
1469
+ shouldUpdateValid) {
1470
+ const updatedFormState = {
1471
+ ...fieldState,
1472
+ ...(shouldUpdateValid && isBoolean(isValid) ? { isValid } : {}),
1473
+ errors: _formState.errors,
1474
+ name,
1475
+ };
1476
+ _formState = {
1477
+ ..._formState,
1478
+ ...updatedFormState,
1479
+ };
1480
+ _subjects.state.next(updatedFormState);
1481
+ }
1345
1482
  };
1346
- return {
1347
- get observers() {
1348
- return _observers;
1349
- },
1350
- next,
1351
- subscribe,
1352
- unsubscribe,
1483
+ const _runSchema = async (name) => {
1484
+ _updateIsValidating(name, true);
1485
+ const result = await _options.resolver(_formValues, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation));
1486
+ _updateIsValidating(name);
1487
+ return result;
1353
1488
  };
1354
- };
1355
-
1356
- var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
1357
-
1358
- function deepEqual(object1, object2) {
1359
- if (isPrimitive(object1) || isPrimitive(object2)) {
1360
- return object1 === object2;
1361
- }
1362
- if (isDateObject(object1) && isDateObject(object2)) {
1363
- return object1.getTime() === object2.getTime();
1364
- }
1365
- const keys1 = Object.keys(object1);
1366
- const keys2 = Object.keys(object2);
1367
- if (keys1.length !== keys2.length) {
1368
- return false;
1369
- }
1370
- for (const key of keys1) {
1371
- const val1 = object1[key];
1372
- if (!keys2.includes(key)) {
1373
- return false;
1374
- }
1375
- if (key !== 'ref') {
1376
- const val2 = object2[key];
1377
- if ((isDateObject(val1) && isDateObject(val2)) ||
1378
- (isObject(val1) && isObject(val2)) ||
1379
- (Array.isArray(val1) && Array.isArray(val2))
1380
- ? !deepEqual(val1, val2)
1381
- : val1 !== val2) {
1382
- return false;
1489
+ const executeSchemaAndUpdateState = async (names) => {
1490
+ const { errors } = await _runSchema(names);
1491
+ if (names) {
1492
+ for (const name of names) {
1493
+ const error = get(errors, name);
1494
+ error
1495
+ ? set(_formState.errors, name, error)
1496
+ : unset(_formState.errors, name);
1383
1497
  }
1384
1498
  }
1385
- }
1386
- return true;
1387
- }
1388
-
1389
- var isMultipleSelect = (element) => element.type === `select-multiple`;
1390
-
1391
- var isRadioOrCheckbox = (ref) => isRadioInput(ref) || isCheckBoxInput(ref);
1392
-
1393
- var live = (ref) => isHTMLElement(ref) && ref.isConnected;
1394
-
1395
- var objectHasFunction = (data) => {
1396
- for (const key in data) {
1397
- if (isFunction(data[key])) {
1398
- return true;
1499
+ else {
1500
+ _formState.errors = errors;
1399
1501
  }
1400
- }
1401
- return false;
1402
- };
1403
-
1404
- function markFieldsDirty(data, fields = {}) {
1405
- const isParentNodeArray = Array.isArray(data);
1406
- if (isObject(data) || isParentNodeArray) {
1407
- for (const key in data) {
1408
- if (Array.isArray(data[key]) ||
1409
- (isObject(data[key]) && !objectHasFunction(data[key]))) {
1410
- fields[key] = Array.isArray(data[key]) ? [] : {};
1411
- markFieldsDirty(data[key], fields[key]);
1412
- }
1413
- else if (!isNullOrUndefined(data[key])) {
1414
- fields[key] = true;
1502
+ return errors;
1503
+ };
1504
+ const executeBuiltInValidation = async (fields, shouldOnlyCheckValid, context = {
1505
+ valid: true,
1506
+ }) => {
1507
+ for (const name in fields) {
1508
+ const field = fields[name];
1509
+ if (field) {
1510
+ const { _f, ...fieldValue } = field;
1511
+ if (_f) {
1512
+ const isFieldArrayRoot = _names.array.has(_f.name);
1513
+ const isPromiseFunction = field._f && hasPromiseValidation(field._f);
1514
+ if (isPromiseFunction && _proxyFormState.validatingFields) {
1515
+ _updateIsValidating([name], true);
1516
+ }
1517
+ const fieldError = await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !shouldOnlyCheckValid, isFieldArrayRoot);
1518
+ if (isPromiseFunction && _proxyFormState.validatingFields) {
1519
+ _updateIsValidating([name]);
1520
+ }
1521
+ if (fieldError[_f.name]) {
1522
+ context.valid = false;
1523
+ if (shouldOnlyCheckValid) {
1524
+ break;
1525
+ }
1526
+ }
1527
+ !shouldOnlyCheckValid &&
1528
+ (get(fieldError, _f.name)
1529
+ ? isFieldArrayRoot
1530
+ ? updateFieldArrayRootError(_formState.errors, fieldError, _f.name)
1531
+ : set(_formState.errors, _f.name, fieldError[_f.name])
1532
+ : unset(_formState.errors, _f.name));
1533
+ }
1534
+ !isEmptyObject(fieldValue) &&
1535
+ (await executeBuiltInValidation(fieldValue, shouldOnlyCheckValid, context));
1415
1536
  }
1416
1537
  }
1417
- }
1418
- return fields;
1419
- }
1420
- function getDirtyFieldsFromDefaultValues(data, formValues, dirtyFieldsFromValues) {
1421
- const isParentNodeArray = Array.isArray(data);
1422
- if (isObject(data) || isParentNodeArray) {
1423
- for (const key in data) {
1424
- if (Array.isArray(data[key]) ||
1425
- (isObject(data[key]) && !objectHasFunction(data[key]))) {
1426
- if (isUndefined(formValues) ||
1427
- isPrimitive(dirtyFieldsFromValues[key])) {
1428
- dirtyFieldsFromValues[key] = Array.isArray(data[key])
1429
- ? markFieldsDirty(data[key], [])
1430
- : { ...markFieldsDirty(data[key]) };
1538
+ return context.valid;
1539
+ };
1540
+ const _removeUnmounted = () => {
1541
+ for (const name of _names.unMount) {
1542
+ const field = get(_fields, name);
1543
+ field &&
1544
+ (field._f.refs
1545
+ ? field._f.refs.every((ref) => !live(ref))
1546
+ : !live(field._f.ref)) &&
1547
+ unregister(name);
1548
+ }
1549
+ _names.unMount = new Set();
1550
+ };
1551
+ const _getDirty = (name, data) => !_options.disabled &&
1552
+ (name && data && set(_formValues, name, data),
1553
+ !deepEqual(getValues(), _defaultValues));
1554
+ const _getWatch = (names, defaultValue, isGlobal) => generateWatchOutput(names, _names, {
1555
+ ...(_state.mount
1556
+ ? _formValues
1557
+ : isUndefined(defaultValue)
1558
+ ? _defaultValues
1559
+ : isString(names)
1560
+ ? { [names]: defaultValue }
1561
+ : defaultValue),
1562
+ }, isGlobal, defaultValue);
1563
+ const _getFieldArray = (name) => compact(get(_state.mount ? _formValues : _defaultValues, name, _options.shouldUnregister ? get(_defaultValues, name, []) : []));
1564
+ const setFieldValue = (name, value, options = {}) => {
1565
+ const field = get(_fields, name);
1566
+ let fieldValue = value;
1567
+ if (field) {
1568
+ const fieldReference = field._f;
1569
+ if (fieldReference) {
1570
+ !fieldReference.disabled &&
1571
+ set(_formValues, name, getFieldValueAs(value, fieldReference));
1572
+ fieldValue =
1573
+ isHTMLElement(fieldReference.ref) && isNullOrUndefined(value)
1574
+ ? ''
1575
+ : value;
1576
+ if (isMultipleSelect(fieldReference.ref)) {
1577
+ [...fieldReference.ref.options].forEach((optionRef) => (optionRef.selected = fieldValue.includes(optionRef.value)));
1578
+ }
1579
+ else if (fieldReference.refs) {
1580
+ if (isCheckBoxInput(fieldReference.ref)) {
1581
+ fieldReference.refs.length > 1
1582
+ ? fieldReference.refs.forEach((checkboxRef) => (!checkboxRef.defaultChecked || !checkboxRef.disabled) &&
1583
+ (checkboxRef.checked = Array.isArray(fieldValue)
1584
+ ? !!fieldValue.find((data) => data === checkboxRef.value)
1585
+ : fieldValue === checkboxRef.value))
1586
+ : fieldReference.refs[0] &&
1587
+ (fieldReference.refs[0].checked = !!fieldValue);
1588
+ }
1589
+ else {
1590
+ fieldReference.refs.forEach((radioRef) => (radioRef.checked = radioRef.value === fieldValue));
1591
+ }
1592
+ }
1593
+ else if (isFileInput(fieldReference.ref)) {
1594
+ fieldReference.ref.value = '';
1431
1595
  }
1432
1596
  else {
1433
- getDirtyFieldsFromDefaultValues(data[key], isNullOrUndefined(formValues) ? {} : formValues[key], dirtyFieldsFromValues[key]);
1597
+ fieldReference.ref.value = fieldValue;
1598
+ if (!fieldReference.ref.type) {
1599
+ _subjects.state.next({
1600
+ name,
1601
+ values: { ..._formValues },
1602
+ });
1603
+ }
1434
1604
  }
1435
1605
  }
1436
- else {
1437
- dirtyFieldsFromValues[key] = !deepEqual(data[key], formValues[key]);
1438
- }
1439
1606
  }
1440
- }
1441
- return dirtyFieldsFromValues;
1442
- }
1443
- var getDirtyFields = (defaultValues, formValues) => getDirtyFieldsFromDefaultValues(defaultValues, formValues, markFieldsDirty(formValues));
1444
-
1445
- var getFieldValueAs = (value, { valueAsNumber, valueAsDate, setValueAs }) => isUndefined(value)
1446
- ? value
1447
- : valueAsNumber
1448
- ? value === ''
1449
- ? NaN
1450
- : value
1451
- ? +value
1452
- : value
1453
- : valueAsDate && isString(value)
1454
- ? new Date(value)
1455
- : setValueAs
1456
- ? setValueAs(value)
1457
- : value;
1458
-
1459
- function getFieldValue(_f) {
1460
- const ref = _f.ref;
1461
- if (_f.refs ? _f.refs.every((ref) => ref.disabled) : ref.disabled) {
1462
- return;
1463
- }
1464
- if (isFileInput(ref)) {
1465
- return ref.files;
1466
- }
1467
- if (isRadioInput(ref)) {
1468
- return getRadioValue(_f.refs).value;
1469
- }
1470
- if (isMultipleSelect(ref)) {
1471
- return [...ref.selectedOptions].map(({ value }) => value);
1472
- }
1473
- if (isCheckBoxInput(ref)) {
1474
- return getCheckboxValue(_f.refs).value;
1475
- }
1476
- return getFieldValueAs(isUndefined(ref.value) ? _f.ref.value : ref.value, _f);
1477
- }
1478
-
1479
- var getResolverOptions = (fieldsNames, _fields, criteriaMode, shouldUseNativeValidation) => {
1480
- const fields = {};
1481
- for (const name of fieldsNames) {
1482
- const field = get(_fields, name);
1483
- field && set(fields, name, field._f);
1484
- }
1485
- return {
1486
- criteriaMode,
1487
- names: [...fieldsNames],
1488
- fields,
1489
- shouldUseNativeValidation,
1607
+ (options.shouldDirty || options.shouldTouch) &&
1608
+ updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty, true);
1609
+ options.shouldValidate && trigger(name);
1490
1610
  };
1491
- };
1492
-
1493
- var getRuleValue = (rule) => isUndefined(rule)
1494
- ? rule
1495
- : isRegex(rule)
1496
- ? rule.source
1497
- : isObject(rule)
1498
- ? isRegex(rule.value)
1499
- ? rule.value.source
1500
- : rule.value
1501
- : rule;
1502
-
1503
- const ASYNC_FUNCTION = 'AsyncFunction';
1504
- var hasPromiseValidation = (fieldReference) => !!fieldReference &&
1505
- !!fieldReference.validate &&
1506
- !!((isFunction(fieldReference.validate) &&
1507
- fieldReference.validate.constructor.name === ASYNC_FUNCTION) ||
1508
- (isObject(fieldReference.validate) &&
1509
- Object.values(fieldReference.validate).find((validateFunction) => validateFunction.constructor.name === ASYNC_FUNCTION)));
1510
-
1511
- var hasValidation = (options) => options.mount &&
1512
- (options.required ||
1513
- options.min ||
1514
- options.max ||
1515
- options.maxLength ||
1516
- options.minLength ||
1517
- options.pattern ||
1518
- options.validate);
1519
-
1520
- function schemaErrorLookup(errors, _fields, name) {
1521
- const error = get(errors, name);
1522
- if (error || isKey(name)) {
1523
- return {
1524
- error,
1525
- name,
1526
- };
1527
- }
1528
- const names = name.split('.');
1529
- while (names.length) {
1530
- const fieldName = names.join('.');
1531
- const field = get(_fields, fieldName);
1532
- const foundError = get(errors, fieldName);
1533
- if (field && !Array.isArray(field) && name !== fieldName) {
1534
- return { name };
1535
- }
1536
- if (foundError && foundError.type) {
1537
- return {
1538
- name: fieldName,
1539
- error: foundError,
1540
- };
1611
+ const setValues = (name, value, options) => {
1612
+ for (const fieldKey in value) {
1613
+ const fieldValue = value[fieldKey];
1614
+ const fieldName = `${name}.${fieldKey}`;
1615
+ const field = get(_fields, fieldName);
1616
+ (_names.array.has(name) ||
1617
+ isObject(fieldValue) ||
1618
+ (field && !field._f)) &&
1619
+ !isDateObject(fieldValue)
1620
+ ? setValues(fieldName, fieldValue, options)
1621
+ : setFieldValue(fieldName, fieldValue, options);
1541
1622
  }
1542
- names.pop();
1543
- }
1544
- return {
1545
- name,
1546
1623
  };
1547
- }
1548
-
1549
- var skipValidation = (isBlurEvent, isTouched, isSubmitted, reValidateMode, mode) => {
1550
- if (mode.isOnAll) {
1551
- return false;
1552
- }
1553
- else if (!isSubmitted && mode.isOnTouch) {
1554
- return !(isTouched || isBlurEvent);
1555
- }
1556
- else if (isSubmitted ? reValidateMode.isOnBlur : mode.isOnBlur) {
1557
- return !isBlurEvent;
1558
- }
1559
- else if (isSubmitted ? reValidateMode.isOnChange : mode.isOnChange) {
1560
- return isBlurEvent;
1561
- }
1562
- return true;
1563
- };
1564
-
1565
- var unsetEmptyArray = (ref, name) => !compact(get(ref, name)).length && unset(ref, name);
1566
-
1567
- const defaultOptions = {
1568
- mode: VALIDATION_MODE.onSubmit,
1569
- reValidateMode: VALIDATION_MODE.onChange,
1570
- shouldFocusError: true,
1571
- };
1572
- function createFormControl(props = {}) {
1573
- let _options = {
1574
- ...defaultOptions,
1575
- ...props,
1576
- };
1577
- let _formState = {
1578
- submitCount: 0,
1579
- isDirty: false,
1580
- isLoading: isFunction(_options.defaultValues),
1581
- isValidating: false,
1582
- isSubmitted: false,
1583
- isSubmitting: false,
1584
- isSubmitSuccessful: false,
1585
- isValid: false,
1586
- touchedFields: {},
1587
- dirtyFields: {},
1588
- validatingFields: {},
1589
- errors: _options.errors || {},
1590
- disabled: _options.disabled || false,
1591
- };
1592
- let _fields = {};
1593
- let _defaultValues = isObject(_options.defaultValues) || isObject(_options.values)
1594
- ? cloneObject(_options.defaultValues || _options.values) || {}
1595
- : {};
1596
- let _formValues = _options.shouldUnregister
1597
- ? {}
1598
- : cloneObject(_defaultValues);
1599
- let _state = {
1600
- action: false,
1601
- mount: false,
1602
- watch: false,
1603
- };
1604
- let _names = {
1605
- mount: new Set(),
1606
- unMount: new Set(),
1607
- array: new Set(),
1608
- watch: new Set(),
1609
- };
1610
- let delayErrorCallback;
1611
- let timer = 0;
1612
- const _proxyFormState = {
1613
- isDirty: false,
1614
- dirtyFields: false,
1615
- validatingFields: false,
1616
- touchedFields: false,
1617
- isValidating: false,
1618
- isValid: false,
1619
- errors: false,
1620
- };
1621
- const _subjects = {
1622
- values: createSubject(),
1623
- array: createSubject(),
1624
- state: createSubject(),
1625
- };
1626
- const validationModeBeforeSubmit = getValidationModes(_options.mode);
1627
- const validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
1628
- const shouldDisplayAllAssociatedErrors = _options.criteriaMode === VALIDATION_MODE.all;
1629
- const debounce = (callback) => (wait) => {
1630
- clearTimeout(timer);
1631
- timer = setTimeout(callback, wait);
1632
- };
1633
- const _updateValid = async (shouldUpdateValid) => {
1634
- if (!_options.disabled && (_proxyFormState.isValid || shouldUpdateValid)) {
1635
- const isValid = _options.resolver
1636
- ? isEmptyObject((await _executeSchema()).errors)
1637
- : await executeBuiltInValidation(_fields, true);
1638
- if (isValid !== _formState.isValid) {
1624
+ const setValue = (name, value, options = {}) => {
1625
+ const field = get(_fields, name);
1626
+ const isFieldArray = _names.array.has(name);
1627
+ const cloneValue = cloneObject(value);
1628
+ set(_formValues, name, cloneValue);
1629
+ if (isFieldArray) {
1630
+ _subjects.array.next({
1631
+ name,
1632
+ values: { ..._formValues },
1633
+ });
1634
+ if ((_proxyFormState.isDirty ||
1635
+ _proxyFormState.dirtyFields ||
1636
+ _proxySubscribeFormState.isDirty ||
1637
+ _proxySubscribeFormState.dirtyFields) &&
1638
+ options.shouldDirty) {
1639
1639
  _subjects.state.next({
1640
- isValid,
1640
+ name,
1641
+ dirtyFields: getDirtyFields(_defaultValues, _formValues),
1642
+ isDirty: _getDirty(name, cloneValue),
1641
1643
  });
1642
1644
  }
1643
1645
  }
1644
- };
1645
- const _updateIsValidating = (names, isValidating) => {
1646
- if (!_options.disabled &&
1647
- (_proxyFormState.isValidating || _proxyFormState.validatingFields)) {
1648
- (names || Array.from(_names.mount)).forEach((name) => {
1649
- if (name) {
1650
- isValidating
1651
- ? set(_formState.validatingFields, name, isValidating)
1652
- : unset(_formState.validatingFields, name);
1653
- }
1654
- });
1655
- _subjects.state.next({
1656
- validatingFields: _formState.validatingFields,
1657
- isValidating: !isEmptyObject(_formState.validatingFields),
1658
- });
1646
+ else {
1647
+ field && !field._f && !isNullOrUndefined(cloneValue)
1648
+ ? setValues(name, cloneValue, options)
1649
+ : setFieldValue(name, cloneValue, options);
1659
1650
  }
1651
+ isWatched(name, _names) && _subjects.state.next({ ..._formState });
1652
+ _subjects.state.next({
1653
+ name: _state.mount ? name : undefined,
1654
+ values: { ..._formValues },
1655
+ });
1660
1656
  };
1661
- const _updateFieldArray = (name, values = [], method, args, shouldSetValues = true, shouldUpdateFieldsAndState = true) => {
1662
- if (args && method && !_options.disabled) {
1663
- _state.action = true;
1664
- if (shouldUpdateFieldsAndState && Array.isArray(get(_fields, name))) {
1665
- const fieldValues = method(get(_fields, name), args.argA, args.argB);
1666
- shouldSetValues && set(_fields, name, fieldValues);
1657
+ const onChange = async (event) => {
1658
+ _state.mount = true;
1659
+ const target = event.target;
1660
+ let name = target.name;
1661
+ let isFieldValueUpdated = true;
1662
+ const field = get(_fields, name);
1663
+ const _updateIsFieldValueUpdated = (fieldValue) => {
1664
+ isFieldValueUpdated =
1665
+ Number.isNaN(fieldValue) ||
1666
+ (isDateObject(fieldValue) && isNaN(fieldValue.getTime())) ||
1667
+ deepEqual(fieldValue, get(_formValues, name, fieldValue));
1668
+ };
1669
+ if (field) {
1670
+ let error;
1671
+ let isValid;
1672
+ const fieldValue = target.type
1673
+ ? getFieldValue(field._f)
1674
+ : getEventValue(event);
1675
+ const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
1676
+ const shouldSkipValidation = (!hasValidation(field._f) &&
1677
+ !_options.resolver &&
1678
+ !get(_formState.errors, name) &&
1679
+ !field._f.deps) ||
1680
+ skipValidation(isBlurEvent, get(_formState.touchedFields, name), _formState.isSubmitted, validationModeAfterSubmit, validationModeBeforeSubmit);
1681
+ const watched = isWatched(name, _names, isBlurEvent);
1682
+ set(_formValues, name, fieldValue);
1683
+ if (isBlurEvent) {
1684
+ field._f.onBlur && field._f.onBlur(event);
1685
+ delayErrorCallback && delayErrorCallback(0);
1667
1686
  }
1668
- if (shouldUpdateFieldsAndState &&
1669
- Array.isArray(get(_formState.errors, name))) {
1670
- const errors = method(get(_formState.errors, name), args.argA, args.argB);
1671
- shouldSetValues && set(_formState.errors, name, errors);
1672
- unsetEmptyArray(_formState.errors, name);
1687
+ else if (field._f.onChange) {
1688
+ field._f.onChange(event);
1673
1689
  }
1674
- if (_proxyFormState.touchedFields &&
1675
- shouldUpdateFieldsAndState &&
1676
- Array.isArray(get(_formState.touchedFields, name))) {
1677
- const touchedFields = method(get(_formState.touchedFields, name), args.argA, args.argB);
1678
- shouldSetValues && set(_formState.touchedFields, name, touchedFields);
1690
+ const fieldState = updateTouchAndDirty(name, fieldValue, isBlurEvent);
1691
+ const shouldRender = !isEmptyObject(fieldState) || watched;
1692
+ !isBlurEvent &&
1693
+ _subjects.state.next({
1694
+ name,
1695
+ type: event.type,
1696
+ values: { ..._formValues },
1697
+ });
1698
+ if (shouldSkipValidation) {
1699
+ if (_proxyFormState.isValid || _proxySubscribeFormState.isValid) {
1700
+ if (_options.mode === 'onBlur' && isBlurEvent) {
1701
+ _setValid();
1702
+ }
1703
+ else if (!isBlurEvent) {
1704
+ _setValid();
1705
+ }
1706
+ }
1707
+ return (shouldRender &&
1708
+ _subjects.state.next({ name, ...(watched ? {} : fieldState) }));
1679
1709
  }
1680
- if (_proxyFormState.dirtyFields) {
1681
- _formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
1710
+ !isBlurEvent && watched && _subjects.state.next({ ..._formState });
1711
+ if (_options.resolver) {
1712
+ const { errors } = await _runSchema([name]);
1713
+ _updateIsFieldValueUpdated(fieldValue);
1714
+ if (isFieldValueUpdated) {
1715
+ const previousErrorLookupResult = schemaErrorLookup(_formState.errors, _fields, name);
1716
+ const errorLookupResult = schemaErrorLookup(errors, _fields, previousErrorLookupResult.name || name);
1717
+ error = errorLookupResult.error;
1718
+ name = errorLookupResult.name;
1719
+ isValid = isEmptyObject(errors);
1720
+ }
1721
+ }
1722
+ else {
1723
+ _updateIsValidating([name], true);
1724
+ error = (await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation))[name];
1725
+ _updateIsValidating([name]);
1726
+ _updateIsFieldValueUpdated(fieldValue);
1727
+ if (isFieldValueUpdated) {
1728
+ if (error) {
1729
+ isValid = false;
1730
+ }
1731
+ else if (_proxyFormState.isValid ||
1732
+ _proxySubscribeFormState.isValid) {
1733
+ isValid = await executeBuiltInValidation(_fields, true);
1734
+ }
1735
+ }
1736
+ }
1737
+ if (isFieldValueUpdated) {
1738
+ field._f.deps &&
1739
+ trigger(field._f.deps);
1740
+ shouldRenderByError(name, isValid, error, fieldState);
1682
1741
  }
1683
- _subjects.state.next({
1684
- name,
1685
- isDirty: _getDirty(name, values),
1686
- dirtyFields: _formState.dirtyFields,
1687
- errors: _formState.errors,
1688
- isValid: _formState.isValid,
1689
- });
1690
1742
  }
1691
- else {
1692
- set(_formValues, name, values);
1743
+ };
1744
+ const _focusInput = (ref, key) => {
1745
+ if (get(_formState.errors, key) && ref.focus) {
1746
+ ref.focus();
1747
+ return 1;
1693
1748
  }
1749
+ return;
1694
1750
  };
1695
- const updateErrors = (name, error) => {
1696
- set(_formState.errors, name, error);
1751
+ const trigger = async (name, options = {}) => {
1752
+ let isValid;
1753
+ let validationResult;
1754
+ const fieldNames = convertToArrayPayload(name);
1755
+ if (_options.resolver) {
1756
+ const errors = await executeSchemaAndUpdateState(isUndefined(name) ? name : fieldNames);
1757
+ isValid = isEmptyObject(errors);
1758
+ validationResult = name
1759
+ ? !fieldNames.some((name) => get(errors, name))
1760
+ : isValid;
1761
+ }
1762
+ else if (name) {
1763
+ validationResult = (await Promise.all(fieldNames.map(async (fieldName) => {
1764
+ const field = get(_fields, fieldName);
1765
+ return await executeBuiltInValidation(field && field._f ? { [fieldName]: field } : field);
1766
+ }))).every(Boolean);
1767
+ !(!validationResult && !_formState.isValid) && _setValid();
1768
+ }
1769
+ else {
1770
+ validationResult = isValid = await executeBuiltInValidation(_fields);
1771
+ }
1697
1772
  _subjects.state.next({
1773
+ ...(!isString(name) ||
1774
+ ((_proxyFormState.isValid || _proxySubscribeFormState.isValid) &&
1775
+ isValid !== _formState.isValid)
1776
+ ? {}
1777
+ : { name }),
1778
+ ...(_options.resolver || !name ? { isValid } : {}),
1698
1779
  errors: _formState.errors,
1699
1780
  });
1781
+ options.shouldFocus &&
1782
+ !validationResult &&
1783
+ iterateFieldsByAction(_fields, _focusInput, name ? fieldNames : _names.mount);
1784
+ return validationResult;
1700
1785
  };
1701
- const _setErrors = (errors) => {
1702
- _formState.errors = errors;
1703
- _subjects.state.next({
1704
- errors: _formState.errors,
1705
- isValid: false,
1706
- });
1786
+ const getValues = (fieldNames) => {
1787
+ const values = {
1788
+ ...(_state.mount ? _formValues : _defaultValues),
1789
+ };
1790
+ return isUndefined(fieldNames)
1791
+ ? values
1792
+ : isString(fieldNames)
1793
+ ? get(values, fieldNames)
1794
+ : fieldNames.map((name) => get(values, name));
1707
1795
  };
1708
- const updateValidAndValue = (name, shouldSkipSetValueAs, value, ref) => {
1709
- const field = get(_fields, name);
1710
- if (field) {
1711
- const defaultValue = get(_formValues, name, isUndefined(value) ? get(_defaultValues, name) : value);
1712
- isUndefined(defaultValue) ||
1713
- (ref && ref.defaultChecked) ||
1714
- shouldSkipSetValueAs
1715
- ? set(_formValues, name, shouldSkipSetValueAs ? defaultValue : getFieldValue(field._f))
1716
- : setFieldValue(name, defaultValue);
1717
- _state.mount && _updateValid();
1718
- }
1796
+ const getFieldState = (name, formState) => ({
1797
+ invalid: !!get((formState || _formState).errors, name),
1798
+ isDirty: !!get((formState || _formState).dirtyFields, name),
1799
+ error: get((formState || _formState).errors, name),
1800
+ isValidating: !!get(_formState.validatingFields, name),
1801
+ isTouched: !!get((formState || _formState).touchedFields, name),
1802
+ });
1803
+ const clearErrors = (name) => {
1804
+ name &&
1805
+ convertToArrayPayload(name).forEach((inputName) => unset(_formState.errors, inputName));
1806
+ _subjects.state.next({
1807
+ errors: name ? _formState.errors : {},
1808
+ });
1719
1809
  };
1720
- const updateTouchAndDirty = (name, fieldValue, isBlurEvent, shouldDirty, shouldRender) => {
1721
- let shouldUpdateField = false;
1722
- let isPreviousDirty = false;
1723
- const output = {
1810
+ const setError = (name, error, options) => {
1811
+ const ref = (get(_fields, name, { _f: {} })._f || {}).ref;
1812
+ const currentError = get(_formState.errors, name) || {};
1813
+ // Don't override existing error messages elsewhere in the object tree.
1814
+ const { ref: currentRef, message, type, ...restOfErrorTree } = currentError;
1815
+ set(_formState.errors, name, {
1816
+ ...restOfErrorTree,
1817
+ ...error,
1818
+ ref,
1819
+ });
1820
+ _subjects.state.next({
1724
1821
  name,
1725
- };
1726
- if (!_options.disabled) {
1727
- const disabledField = !!(get(_fields, name) &&
1728
- get(_fields, name)._f &&
1729
- get(_fields, name)._f.disabled);
1730
- if (!isBlurEvent || shouldDirty) {
1731
- if (_proxyFormState.isDirty) {
1732
- isPreviousDirty = _formState.isDirty;
1733
- _formState.isDirty = output.isDirty = _getDirty();
1734
- shouldUpdateField = isPreviousDirty !== output.isDirty;
1735
- }
1736
- const isCurrentFieldPristine = disabledField || deepEqual(get(_defaultValues, name), fieldValue);
1737
- isPreviousDirty = !!(!disabledField && get(_formState.dirtyFields, name));
1738
- isCurrentFieldPristine || disabledField
1739
- ? unset(_formState.dirtyFields, name)
1740
- : set(_formState.dirtyFields, name, true);
1741
- output.dirtyFields = _formState.dirtyFields;
1742
- shouldUpdateField =
1743
- shouldUpdateField ||
1744
- (_proxyFormState.dirtyFields &&
1745
- isPreviousDirty !== !isCurrentFieldPristine);
1822
+ errors: _formState.errors,
1823
+ isValid: false,
1824
+ });
1825
+ options && options.shouldFocus && ref && ref.focus && ref.focus();
1826
+ };
1827
+ const watch = (name, defaultValue) => isFunction(name)
1828
+ ? _subjects.state.subscribe({
1829
+ next: (payload) => name(_getWatch(undefined, defaultValue), payload),
1830
+ })
1831
+ : _getWatch(name, defaultValue, true);
1832
+ const _subscribe = (props) => _subjects.state.subscribe({
1833
+ next: (formState) => {
1834
+ if (shouldSubscribeByName(props.name, formState.name, props.exact) &&
1835
+ shouldRenderFormState(formState, props.formState || _proxyFormState, _setFormState, props.reRenderRoot)) {
1836
+ props.callback({
1837
+ values: _formValues,
1838
+ ..._formState,
1839
+ ...formState,
1840
+ });
1746
1841
  }
1747
- if (isBlurEvent) {
1748
- const isPreviousFieldTouched = get(_formState.touchedFields, name);
1749
- if (!isPreviousFieldTouched) {
1750
- set(_formState.touchedFields, name, isBlurEvent);
1751
- output.touchedFields = _formState.touchedFields;
1752
- shouldUpdateField =
1753
- shouldUpdateField ||
1754
- (_proxyFormState.touchedFields &&
1755
- isPreviousFieldTouched !== isBlurEvent);
1756
- }
1842
+ },
1843
+ }).unsubscribe;
1844
+ const subscribe = (props) => {
1845
+ _state.mount = true;
1846
+ _proxySubscribeFormState = {
1847
+ ..._proxySubscribeFormState,
1848
+ ...props.formState,
1849
+ };
1850
+ return _subscribe({
1851
+ ...props,
1852
+ formState: _proxySubscribeFormState,
1853
+ });
1854
+ };
1855
+ const unregister = (name, options = {}) => {
1856
+ for (const fieldName of name ? convertToArrayPayload(name) : _names.mount) {
1857
+ _names.mount.delete(fieldName);
1858
+ _names.array.delete(fieldName);
1859
+ if (!options.keepValue) {
1860
+ unset(_fields, fieldName);
1861
+ unset(_formValues, fieldName);
1757
1862
  }
1758
- shouldUpdateField && shouldRender && _subjects.state.next(output);
1863
+ !options.keepError && unset(_formState.errors, fieldName);
1864
+ !options.keepDirty && unset(_formState.dirtyFields, fieldName);
1865
+ !options.keepTouched && unset(_formState.touchedFields, fieldName);
1866
+ !options.keepIsValidating &&
1867
+ unset(_formState.validatingFields, fieldName);
1868
+ !_options.shouldUnregister &&
1869
+ !options.keepDefaultValue &&
1870
+ unset(_defaultValues, fieldName);
1759
1871
  }
1760
- return shouldUpdateField ? output : {};
1872
+ _subjects.state.next({
1873
+ values: { ..._formValues },
1874
+ });
1875
+ _subjects.state.next({
1876
+ ..._formState,
1877
+ ...(!options.keepDirty ? {} : { isDirty: _getDirty() }),
1878
+ });
1879
+ !options.keepIsValid && _setValid();
1761
1880
  };
1762
- const shouldRenderByError = (name, isValid, error, fieldState) => {
1763
- const previousFieldError = get(_formState.errors, name);
1764
- const shouldUpdateValid = _proxyFormState.isValid &&
1765
- isBoolean(isValid) &&
1766
- _formState.isValid !== isValid;
1767
- if (_options.delayError && error) {
1768
- delayErrorCallback = debounce(() => updateErrors(name, error));
1769
- delayErrorCallback(_options.delayError);
1770
- }
1771
- else {
1772
- clearTimeout(timer);
1773
- delayErrorCallback = null;
1774
- error
1775
- ? set(_formState.errors, name, error)
1776
- : unset(_formState.errors, name);
1777
- }
1778
- if ((error ? !deepEqual(previousFieldError, error) : previousFieldError) ||
1779
- !isEmptyObject(fieldState) ||
1780
- shouldUpdateValid) {
1781
- const updatedFormState = {
1782
- ...fieldState,
1783
- ...(shouldUpdateValid && isBoolean(isValid) ? { isValid } : {}),
1784
- errors: _formState.errors,
1785
- name,
1786
- };
1787
- _formState = {
1788
- ..._formState,
1789
- ...updatedFormState,
1790
- };
1791
- _subjects.state.next(updatedFormState);
1881
+ const _setDisabledField = ({ disabled, name, field, fields, }) => {
1882
+ if ((isBoolean(disabled) && _state.mount) ||
1883
+ !!disabled ||
1884
+ _names.disabled.has(name)) {
1885
+ disabled ? _names.disabled.add(name) : _names.disabled.delete(name);
1886
+ updateTouchAndDirty(name, getFieldValue(field ? field._f : get(fields, name)._f), false, false, true);
1792
1887
  }
1793
1888
  };
1794
- const _executeSchema = async (name) => {
1795
- _updateIsValidating(name, true);
1796
- const result = await _options.resolver(_formValues, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation));
1797
- _updateIsValidating(name);
1798
- return result;
1799
- };
1800
- const executeSchemaAndUpdateState = async (names) => {
1801
- const { errors } = await _executeSchema(names);
1802
- if (names) {
1803
- for (const name of names) {
1804
- const error = get(errors, name);
1805
- error
1806
- ? set(_formState.errors, name, error)
1807
- : unset(_formState.errors, name);
1808
- }
1889
+ const register = (name, options = {}) => {
1890
+ let field = get(_fields, name);
1891
+ const disabledIsDefined = isBoolean(options.disabled) || isBoolean(_options.disabled);
1892
+ set(_fields, name, {
1893
+ ...(field || {}),
1894
+ _f: {
1895
+ ...(field && field._f ? field._f : { ref: { name } }),
1896
+ name,
1897
+ mount: true,
1898
+ ...options,
1899
+ },
1900
+ });
1901
+ _names.mount.add(name);
1902
+ if (field) {
1903
+ _setDisabledField({
1904
+ field,
1905
+ disabled: isBoolean(options.disabled)
1906
+ ? options.disabled
1907
+ : _options.disabled,
1908
+ name,
1909
+ });
1809
1910
  }
1810
1911
  else {
1811
- _formState.errors = errors;
1912
+ updateValidAndValue(name, true, options.value);
1812
1913
  }
1813
- return errors;
1814
- };
1815
- const executeBuiltInValidation = async (fields, shouldOnlyCheckValid, context = {
1816
- valid: true,
1817
- }) => {
1818
- for (const name in fields) {
1819
- const field = fields[name];
1820
- if (field) {
1821
- const { _f, ...fieldValue } = field;
1822
- if (_f) {
1823
- const isFieldArrayRoot = _names.array.has(_f.name);
1824
- const isPromiseFunction = field._f && hasPromiseValidation(field._f);
1825
- if (isPromiseFunction && _proxyFormState.validatingFields) {
1826
- _updateIsValidating([name], true);
1827
- }
1828
- const fieldError = await validateField(field, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !shouldOnlyCheckValid, isFieldArrayRoot);
1829
- if (isPromiseFunction && _proxyFormState.validatingFields) {
1830
- _updateIsValidating([name]);
1914
+ return {
1915
+ ...(disabledIsDefined
1916
+ ? { disabled: options.disabled || _options.disabled }
1917
+ : {}),
1918
+ ...(_options.progressive
1919
+ ? {
1920
+ required: !!options.required,
1921
+ min: getRuleValue(options.min),
1922
+ max: getRuleValue(options.max),
1923
+ minLength: getRuleValue(options.minLength),
1924
+ maxLength: getRuleValue(options.maxLength),
1925
+ pattern: getRuleValue(options.pattern),
1926
+ }
1927
+ : {}),
1928
+ name,
1929
+ onChange,
1930
+ onBlur: onChange,
1931
+ ref: (ref) => {
1932
+ if (ref) {
1933
+ register(name, options);
1934
+ field = get(_fields, name);
1935
+ const fieldRef = isUndefined(ref.value)
1936
+ ? ref.querySelectorAll
1937
+ ? ref.querySelectorAll('input,select,textarea')[0] || ref
1938
+ : ref
1939
+ : ref;
1940
+ const radioOrCheckbox = isRadioOrCheckbox(fieldRef);
1941
+ const refs = field._f.refs || [];
1942
+ if (radioOrCheckbox
1943
+ ? refs.find((option) => option === fieldRef)
1944
+ : fieldRef === field._f.ref) {
1945
+ return;
1831
1946
  }
1832
- if (fieldError[_f.name]) {
1833
- context.valid = false;
1834
- if (shouldOnlyCheckValid) {
1835
- break;
1836
- }
1947
+ set(_fields, name, {
1948
+ _f: {
1949
+ ...field._f,
1950
+ ...(radioOrCheckbox
1951
+ ? {
1952
+ refs: [
1953
+ ...refs.filter(live),
1954
+ fieldRef,
1955
+ ...(Array.isArray(get(_defaultValues, name)) ? [{}] : []),
1956
+ ],
1957
+ ref: { type: fieldRef.type, name },
1958
+ }
1959
+ : { ref: fieldRef }),
1960
+ },
1961
+ });
1962
+ updateValidAndValue(name, false, undefined, fieldRef);
1963
+ }
1964
+ else {
1965
+ field = get(_fields, name, {});
1966
+ if (field._f) {
1967
+ field._f.mount = false;
1837
1968
  }
1838
- !shouldOnlyCheckValid &&
1839
- (get(fieldError, _f.name)
1840
- ? isFieldArrayRoot
1841
- ? updateFieldArrayRootError(_formState.errors, fieldError, _f.name)
1842
- : set(_formState.errors, _f.name, fieldError[_f.name])
1843
- : unset(_formState.errors, _f.name));
1969
+ (_options.shouldUnregister || options.shouldUnregister) &&
1970
+ !(isNameInFieldArray(_names.array, name) && _state.action) &&
1971
+ _names.unMount.add(name);
1844
1972
  }
1845
- !isEmptyObject(fieldValue) &&
1846
- (await executeBuiltInValidation(fieldValue, shouldOnlyCheckValid, context));
1847
- }
1848
- }
1849
- return context.valid;
1973
+ },
1974
+ };
1850
1975
  };
1851
- const _removeUnmounted = () => {
1852
- for (const name of _names.unMount) {
1853
- const field = get(_fields, name);
1854
- field &&
1855
- (field._f.refs
1856
- ? field._f.refs.every((ref) => !live(ref))
1857
- : !live(field._f.ref)) &&
1858
- unregister(name);
1859
- }
1860
- _names.unMount = new Set();
1861
- };
1862
- const _getDirty = (name, data) => !_options.disabled &&
1863
- (name && data && set(_formValues, name, data),
1864
- !deepEqual(getValues(), _defaultValues));
1865
- const _getWatch = (names, defaultValue, isGlobal) => generateWatchOutput(names, _names, {
1866
- ...(_state.mount
1867
- ? _formValues
1868
- : isUndefined(defaultValue)
1869
- ? _defaultValues
1870
- : isString(names)
1871
- ? { [names]: defaultValue }
1872
- : defaultValue),
1873
- }, isGlobal, defaultValue);
1874
- const _getFieldArray = (name) => compact(get(_state.mount ? _formValues : _defaultValues, name, _options.shouldUnregister ? get(_defaultValues, name, []) : []));
1875
- const setFieldValue = (name, value, options = {}) => {
1876
- const field = get(_fields, name);
1877
- let fieldValue = value;
1878
- if (field) {
1879
- const fieldReference = field._f;
1880
- if (fieldReference) {
1881
- !fieldReference.disabled &&
1882
- set(_formValues, name, getFieldValueAs(value, fieldReference));
1883
- fieldValue =
1884
- isHTMLElement(fieldReference.ref) && isNullOrUndefined(value)
1885
- ? ''
1886
- : value;
1887
- if (isMultipleSelect(fieldReference.ref)) {
1888
- [...fieldReference.ref.options].forEach((optionRef) => (optionRef.selected = fieldValue.includes(optionRef.value)));
1889
- }
1890
- else if (fieldReference.refs) {
1891
- if (isCheckBoxInput(fieldReference.ref)) {
1892
- fieldReference.refs.length > 1
1893
- ? fieldReference.refs.forEach((checkboxRef) => (!checkboxRef.defaultChecked || !checkboxRef.disabled) &&
1894
- (checkboxRef.checked = Array.isArray(fieldValue)
1895
- ? !!fieldValue.find((data) => data === checkboxRef.value)
1896
- : fieldValue === checkboxRef.value))
1897
- : fieldReference.refs[0] &&
1898
- (fieldReference.refs[0].checked = !!fieldValue);
1899
- }
1900
- else {
1901
- fieldReference.refs.forEach((radioRef) => (radioRef.checked = radioRef.value === fieldValue));
1902
- }
1903
- }
1904
- else if (isFileInput(fieldReference.ref)) {
1905
- fieldReference.ref.value = '';
1906
- }
1907
- else {
1908
- fieldReference.ref.value = fieldValue;
1909
- if (!fieldReference.ref.type) {
1910
- _subjects.values.next({
1911
- name,
1912
- values: { ..._formValues },
1976
+ const _focusError = () => _options.shouldFocusError &&
1977
+ iterateFieldsByAction(_fields, _focusInput, _names.mount);
1978
+ const _disableForm = (disabled) => {
1979
+ if (isBoolean(disabled)) {
1980
+ _subjects.state.next({ disabled });
1981
+ iterateFieldsByAction(_fields, (ref, name) => {
1982
+ const currentField = get(_fields, name);
1983
+ if (currentField) {
1984
+ ref.disabled = currentField._f.disabled || disabled;
1985
+ if (Array.isArray(currentField._f.refs)) {
1986
+ currentField._f.refs.forEach((inputRef) => {
1987
+ inputRef.disabled = currentField._f.disabled || disabled;
1913
1988
  });
1914
1989
  }
1915
1990
  }
1916
- }
1991
+ }, 0, false);
1917
1992
  }
1918
- (options.shouldDirty || options.shouldTouch) &&
1919
- updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty, true);
1920
- options.shouldValidate && trigger(name);
1921
1993
  };
1922
- const setValues = (name, value, options) => {
1923
- for (const fieldKey in value) {
1924
- const fieldValue = value[fieldKey];
1925
- const fieldName = `${name}.${fieldKey}`;
1926
- const field = get(_fields, fieldName);
1927
- (_names.array.has(name) ||
1928
- isObject(fieldValue) ||
1929
- (field && !field._f)) &&
1930
- !isDateObject(fieldValue)
1931
- ? setValues(fieldName, fieldValue, options)
1932
- : setFieldValue(fieldName, fieldValue, options);
1994
+ const handleSubmit = (onValid, onInvalid) => async (e) => {
1995
+ let onValidError = undefined;
1996
+ if (e) {
1997
+ e.preventDefault && e.preventDefault();
1998
+ e.persist && e.persist();
1933
1999
  }
1934
- };
1935
- const setValue = (name, value, options = {}) => {
1936
- const field = get(_fields, name);
1937
- const isFieldArray = _names.array.has(name);
1938
- const cloneValue = cloneObject(value);
1939
- set(_formValues, name, cloneValue);
1940
- if (isFieldArray) {
1941
- _subjects.array.next({
1942
- name,
1943
- values: { ..._formValues },
2000
+ let fieldValues = cloneObject(_formValues);
2001
+ if (_names.disabled.size) {
2002
+ for (const name of _names.disabled) {
2003
+ set(fieldValues, name, undefined);
2004
+ }
2005
+ }
2006
+ _subjects.state.next({
2007
+ isSubmitting: true,
2008
+ });
2009
+ if (_options.resolver) {
2010
+ const { errors, values } = await _runSchema();
2011
+ _formState.errors = errors;
2012
+ fieldValues = values;
2013
+ }
2014
+ else {
2015
+ await executeBuiltInValidation(_fields);
2016
+ }
2017
+ unset(_formState.errors, 'root');
2018
+ if (isEmptyObject(_formState.errors)) {
2019
+ _subjects.state.next({
2020
+ errors: {},
1944
2021
  });
1945
- if ((_proxyFormState.isDirty || _proxyFormState.dirtyFields) &&
1946
- options.shouldDirty) {
1947
- _subjects.state.next({
1948
- name,
1949
- dirtyFields: getDirtyFields(_defaultValues, _formValues),
1950
- isDirty: _getDirty(name, cloneValue),
1951
- });
2022
+ try {
2023
+ await onValid(fieldValues, e);
2024
+ }
2025
+ catch (error) {
2026
+ onValidError = error;
1952
2027
  }
1953
2028
  }
1954
2029
  else {
1955
- field && !field._f && !isNullOrUndefined(cloneValue)
1956
- ? setValues(name, cloneValue, options)
1957
- : setFieldValue(name, cloneValue, options);
2030
+ if (onInvalid) {
2031
+ await onInvalid({ ..._formState.errors }, e);
2032
+ }
2033
+ _focusError();
2034
+ setTimeout(_focusError);
1958
2035
  }
1959
- isWatched(name, _names) && _subjects.state.next({ ..._formState });
1960
- _subjects.values.next({
1961
- name: _state.mount ? name : undefined,
1962
- values: { ..._formValues },
2036
+ _subjects.state.next({
2037
+ isSubmitted: true,
2038
+ isSubmitting: false,
2039
+ isSubmitSuccessful: isEmptyObject(_formState.errors) && !onValidError,
2040
+ submitCount: _formState.submitCount + 1,
2041
+ errors: _formState.errors,
1963
2042
  });
2043
+ if (onValidError) {
2044
+ throw onValidError;
2045
+ }
1964
2046
  };
1965
- const onChange = async (event) => {
1966
- _state.mount = true;
1967
- const target = event.target;
1968
- let name = target.name;
1969
- let isFieldValueUpdated = true;
1970
- const field = get(_fields, name);
1971
- const getCurrentFieldValue = () => target.type ? getFieldValue(field._f) : getEventValue(event);
1972
- const _updateIsFieldValueUpdated = (fieldValue) => {
1973
- isFieldValueUpdated =
1974
- Number.isNaN(fieldValue) ||
1975
- (isDateObject(fieldValue) && isNaN(fieldValue.getTime())) ||
1976
- deepEqual(fieldValue, get(_formValues, name, fieldValue));
1977
- };
1978
- if (field) {
1979
- let error;
1980
- let isValid;
1981
- const fieldValue = getCurrentFieldValue();
1982
- const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
1983
- const shouldSkipValidation = (!hasValidation(field._f) &&
1984
- !_options.resolver &&
1985
- !get(_formState.errors, name) &&
1986
- !field._f.deps) ||
1987
- skipValidation(isBlurEvent, get(_formState.touchedFields, name), _formState.isSubmitted, validationModeAfterSubmit, validationModeBeforeSubmit);
1988
- const watched = isWatched(name, _names, isBlurEvent);
1989
- set(_formValues, name, fieldValue);
1990
- if (isBlurEvent) {
1991
- field._f.onBlur && field._f.onBlur(event);
1992
- delayErrorCallback && delayErrorCallback(0);
1993
- }
1994
- else if (field._f.onChange) {
1995
- field._f.onChange(event);
2047
+ const resetField = (name, options = {}) => {
2048
+ if (get(_fields, name)) {
2049
+ if (isUndefined(options.defaultValue)) {
2050
+ setValue(name, cloneObject(get(_defaultValues, name)));
1996
2051
  }
1997
- const fieldState = updateTouchAndDirty(name, fieldValue, isBlurEvent, false);
1998
- const shouldRender = !isEmptyObject(fieldState) || watched;
1999
- !isBlurEvent &&
2000
- _subjects.values.next({
2001
- name,
2002
- type: event.type,
2003
- values: { ..._formValues },
2004
- });
2005
- if (shouldSkipValidation) {
2006
- if (_proxyFormState.isValid) {
2007
- if (_options.mode === 'onBlur') {
2008
- if (isBlurEvent) {
2009
- _updateValid();
2010
- }
2011
- }
2012
- else {
2013
- _updateValid();
2014
- }
2015
- }
2016
- return (shouldRender &&
2017
- _subjects.state.next({ name, ...(watched ? {} : fieldState) }));
2052
+ else {
2053
+ setValue(name, options.defaultValue);
2054
+ set(_defaultValues, name, cloneObject(options.defaultValue));
2018
2055
  }
2019
- !isBlurEvent && watched && _subjects.state.next({ ..._formState });
2020
- if (_options.resolver) {
2021
- const { errors } = await _executeSchema([name]);
2022
- _updateIsFieldValueUpdated(fieldValue);
2023
- if (isFieldValueUpdated) {
2024
- const previousErrorLookupResult = schemaErrorLookup(_formState.errors, _fields, name);
2025
- const errorLookupResult = schemaErrorLookup(errors, _fields, previousErrorLookupResult.name || name);
2026
- error = errorLookupResult.error;
2027
- name = errorLookupResult.name;
2028
- isValid = isEmptyObject(errors);
2029
- }
2056
+ if (!options.keepTouched) {
2057
+ unset(_formState.touchedFields, name);
2030
2058
  }
2031
- else {
2032
- _updateIsValidating([name], true);
2033
- error = (await validateField(field, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation))[name];
2034
- _updateIsValidating([name]);
2035
- _updateIsFieldValueUpdated(fieldValue);
2036
- if (isFieldValueUpdated) {
2037
- if (error) {
2038
- isValid = false;
2039
- }
2040
- else if (_proxyFormState.isValid) {
2041
- isValid = await executeBuiltInValidation(_fields, true);
2042
- }
2043
- }
2059
+ if (!options.keepDirty) {
2060
+ unset(_formState.dirtyFields, name);
2061
+ _formState.isDirty = options.defaultValue
2062
+ ? _getDirty(name, cloneObject(get(_defaultValues, name)))
2063
+ : _getDirty();
2044
2064
  }
2045
- if (isFieldValueUpdated) {
2046
- field._f.deps &&
2047
- trigger(field._f.deps);
2048
- shouldRenderByError(name, isValid, error, fieldState);
2065
+ if (!options.keepError) {
2066
+ unset(_formState.errors, name);
2067
+ _proxyFormState.isValid && _setValid();
2049
2068
  }
2069
+ _subjects.state.next({ ..._formState });
2050
2070
  }
2051
2071
  };
2052
- const _focusInput = (ref, key) => {
2053
- if (get(_formState.errors, key) && ref.focus) {
2054
- ref.focus();
2055
- return 1;
2072
+ const _reset = (formValues, keepStateOptions = {}) => {
2073
+ const updatedValues = formValues ? cloneObject(formValues) : _defaultValues;
2074
+ const cloneUpdatedValues = cloneObject(updatedValues);
2075
+ const isEmptyResetValues = isEmptyObject(formValues);
2076
+ const values = isEmptyResetValues ? _defaultValues : cloneUpdatedValues;
2077
+ if (!keepStateOptions.keepDefaultValues) {
2078
+ _defaultValues = updatedValues;
2056
2079
  }
2057
- return;
2058
- };
2059
- const trigger = async (name, options = {}) => {
2060
- let isValid;
2061
- let validationResult;
2062
- const fieldNames = convertToArrayPayload(name);
2063
- if (_options.resolver) {
2064
- const errors = await executeSchemaAndUpdateState(isUndefined(name) ? name : fieldNames);
2065
- isValid = isEmptyObject(errors);
2066
- validationResult = name
2067
- ? !fieldNames.some((name) => get(errors, name))
2068
- : isValid;
2069
- }
2070
- else if (name) {
2071
- validationResult = (await Promise.all(fieldNames.map(async (fieldName) => {
2072
- const field = get(_fields, fieldName);
2073
- return await executeBuiltInValidation(field && field._f ? { [fieldName]: field } : field);
2074
- }))).every(Boolean);
2075
- !(!validationResult && !_formState.isValid) && _updateValid();
2076
- }
2077
- else {
2078
- validationResult = isValid = await executeBuiltInValidation(_fields);
2080
+ if (!keepStateOptions.keepValues) {
2081
+ if (keepStateOptions.keepDirtyValues) {
2082
+ const fieldsToCheck = new Set([
2083
+ ..._names.mount,
2084
+ ...Object.keys(getDirtyFields(_defaultValues, _formValues)),
2085
+ ]);
2086
+ for (const fieldName of Array.from(fieldsToCheck)) {
2087
+ get(_formState.dirtyFields, fieldName)
2088
+ ? set(values, fieldName, get(_formValues, fieldName))
2089
+ : setValue(fieldName, get(values, fieldName));
2090
+ }
2091
+ }
2092
+ else {
2093
+ if (isWeb && isUndefined(formValues)) {
2094
+ for (const name of _names.mount) {
2095
+ const field = get(_fields, name);
2096
+ if (field && field._f) {
2097
+ const fieldReference = Array.isArray(field._f.refs)
2098
+ ? field._f.refs[0]
2099
+ : field._f.ref;
2100
+ if (isHTMLElement(fieldReference)) {
2101
+ const form = fieldReference.closest('form');
2102
+ if (form) {
2103
+ form.reset();
2104
+ break;
2105
+ }
2106
+ }
2107
+ }
2108
+ }
2109
+ }
2110
+ _fields = {};
2111
+ }
2112
+ _formValues = _options.shouldUnregister
2113
+ ? keepStateOptions.keepDefaultValues
2114
+ ? cloneObject(_defaultValues)
2115
+ : {}
2116
+ : cloneObject(values);
2117
+ _subjects.array.next({
2118
+ values: { ...values },
2119
+ });
2120
+ _subjects.state.next({
2121
+ values: { ...values },
2122
+ });
2079
2123
  }
2080
- _subjects.state.next({
2081
- ...(!isString(name) ||
2082
- (_proxyFormState.isValid && isValid !== _formState.isValid)
2083
- ? {}
2084
- : { name }),
2085
- ...(_options.resolver || !name ? { isValid } : {}),
2086
- errors: _formState.errors,
2087
- });
2088
- options.shouldFocus &&
2089
- !validationResult &&
2090
- iterateFieldsByAction(_fields, _focusInput, name ? fieldNames : _names.mount);
2091
- return validationResult;
2092
- };
2093
- const getValues = (fieldNames) => {
2094
- const values = {
2095
- ...(_state.mount ? _formValues : _defaultValues),
2124
+ _names = {
2125
+ mount: keepStateOptions.keepDirtyValues ? _names.mount : new Set(),
2126
+ unMount: new Set(),
2127
+ array: new Set(),
2128
+ disabled: new Set(),
2129
+ watch: new Set(),
2130
+ watchAll: false,
2131
+ focus: '',
2096
2132
  };
2097
- return isUndefined(fieldNames)
2098
- ? values
2099
- : isString(fieldNames)
2100
- ? get(values, fieldNames)
2101
- : fieldNames.map((name) => get(values, name));
2102
- };
2103
- const getFieldState = (name, formState) => ({
2104
- invalid: !!get((formState || _formState).errors, name),
2105
- isDirty: !!get((formState || _formState).dirtyFields, name),
2106
- error: get((formState || _formState).errors, name),
2107
- isValidating: !!get(_formState.validatingFields, name),
2108
- isTouched: !!get((formState || _formState).touchedFields, name),
2109
- });
2110
- const clearErrors = (name) => {
2111
- name &&
2112
- convertToArrayPayload(name).forEach((inputName) => unset(_formState.errors, inputName));
2113
- _subjects.state.next({
2114
- errors: name ? _formState.errors : {},
2115
- });
2116
- };
2117
- const setError = (name, error, options) => {
2118
- const ref = (get(_fields, name, { _f: {} })._f || {}).ref;
2119
- const currentError = get(_formState.errors, name) || {};
2120
- // Don't override existing error messages elsewhere in the object tree.
2121
- const { ref: currentRef, message, type, ...restOfErrorTree } = currentError;
2122
- set(_formState.errors, name, {
2123
- ...restOfErrorTree,
2124
- ...error,
2125
- ref,
2126
- });
2133
+ _state.mount =
2134
+ !_proxyFormState.isValid ||
2135
+ !!keepStateOptions.keepIsValid ||
2136
+ !!keepStateOptions.keepDirtyValues;
2137
+ _state.watch = !!_options.shouldUnregister;
2127
2138
  _subjects.state.next({
2128
- name,
2129
- errors: _formState.errors,
2130
- isValid: false,
2139
+ submitCount: keepStateOptions.keepSubmitCount
2140
+ ? _formState.submitCount
2141
+ : 0,
2142
+ isDirty: isEmptyResetValues
2143
+ ? false
2144
+ : keepStateOptions.keepDirty
2145
+ ? _formState.isDirty
2146
+ : !!(keepStateOptions.keepDefaultValues &&
2147
+ !deepEqual(formValues, _defaultValues)),
2148
+ isSubmitted: keepStateOptions.keepIsSubmitted
2149
+ ? _formState.isSubmitted
2150
+ : false,
2151
+ dirtyFields: isEmptyResetValues
2152
+ ? {}
2153
+ : keepStateOptions.keepDirtyValues
2154
+ ? keepStateOptions.keepDefaultValues && _formValues
2155
+ ? getDirtyFields(_defaultValues, _formValues)
2156
+ : _formState.dirtyFields
2157
+ : keepStateOptions.keepDefaultValues && formValues
2158
+ ? getDirtyFields(_defaultValues, formValues)
2159
+ : keepStateOptions.keepDirty
2160
+ ? _formState.dirtyFields
2161
+ : {},
2162
+ touchedFields: keepStateOptions.keepTouched
2163
+ ? _formState.touchedFields
2164
+ : {},
2165
+ errors: keepStateOptions.keepErrors ? _formState.errors : {},
2166
+ isSubmitSuccessful: keepStateOptions.keepIsSubmitSuccessful
2167
+ ? _formState.isSubmitSuccessful
2168
+ : false,
2169
+ isSubmitting: false,
2131
2170
  });
2132
- options && options.shouldFocus && ref && ref.focus && ref.focus();
2133
2171
  };
2134
- const watch = (name, defaultValue) => isFunction(name)
2135
- ? _subjects.values.subscribe({
2136
- next: (payload) => name(_getWatch(undefined, defaultValue), payload),
2137
- })
2138
- : _getWatch(name, defaultValue, true);
2139
- const unregister = (name, options = {}) => {
2140
- for (const fieldName of name ? convertToArrayPayload(name) : _names.mount) {
2141
- _names.mount.delete(fieldName);
2142
- _names.array.delete(fieldName);
2143
- if (!options.keepValue) {
2144
- unset(_fields, fieldName);
2145
- unset(_formValues, fieldName);
2172
+ const reset = (formValues, keepStateOptions) => _reset(isFunction(formValues)
2173
+ ? formValues(_formValues)
2174
+ : formValues, keepStateOptions);
2175
+ const setFocus = (name, options = {}) => {
2176
+ const field = get(_fields, name);
2177
+ const fieldReference = field && field._f;
2178
+ if (fieldReference) {
2179
+ const fieldRef = fieldReference.refs
2180
+ ? fieldReference.refs[0]
2181
+ : fieldReference.ref;
2182
+ if (fieldRef.focus) {
2183
+ fieldRef.focus();
2184
+ options.shouldSelect &&
2185
+ isFunction(fieldRef.select) &&
2186
+ fieldRef.select();
2146
2187
  }
2147
- !options.keepError && unset(_formState.errors, fieldName);
2148
- !options.keepDirty && unset(_formState.dirtyFields, fieldName);
2149
- !options.keepTouched && unset(_formState.touchedFields, fieldName);
2150
- !options.keepIsValidating &&
2151
- unset(_formState.validatingFields, fieldName);
2152
- !_options.shouldUnregister &&
2153
- !options.keepDefaultValue &&
2154
- unset(_defaultValues, fieldName);
2155
2188
  }
2156
- _subjects.values.next({
2157
- values: { ..._formValues },
2158
- });
2159
- _subjects.state.next({
2160
- ..._formState,
2161
- ...(!options.keepDirty ? {} : { isDirty: _getDirty() }),
2162
- });
2163
- !options.keepIsValid && _updateValid();
2164
2189
  };
2165
- const _updateDisabledField = ({ disabled, name, field, fields, value, }) => {
2166
- if ((isBoolean(disabled) && _state.mount) || !!disabled) {
2167
- const inputValue = disabled
2168
- ? undefined
2169
- : isUndefined(value)
2170
- ? getFieldValue(field ? field._f : get(fields, name)._f)
2171
- : value;
2172
- if (disabled || (!disabled && !isUndefined(inputValue))) {
2173
- set(_formValues, name, inputValue);
2174
- }
2175
- updateTouchAndDirty(name, inputValue, false, false, true);
2176
- }
2190
+ const _setFormState = (updatedFormState) => {
2191
+ _formState = {
2192
+ ..._formState,
2193
+ ...updatedFormState,
2194
+ };
2177
2195
  };
2178
- const register = (name, options = {}) => {
2179
- let field = get(_fields, name);
2180
- const disabledIsDefined = isBoolean(options.disabled) || isBoolean(_options.disabled);
2181
- set(_fields, name, {
2182
- ...(field || {}),
2183
- _f: {
2184
- ...(field && field._f ? field._f : { ref: { name } }),
2185
- name,
2186
- mount: true,
2187
- ...options,
2188
- },
2189
- });
2190
- _names.mount.add(name);
2191
- if (field) {
2192
- _updateDisabledField({
2193
- field,
2194
- disabled: isBoolean(options.disabled)
2195
- ? options.disabled
2196
- : _options.disabled,
2197
- name,
2198
- value: options.value,
2196
+ const _resetDefaultValues = () => isFunction(_options.defaultValues) &&
2197
+ _options.defaultValues().then((values) => {
2198
+ reset(values, _options.resetOptions);
2199
+ _subjects.state.next({
2200
+ isLoading: false,
2199
2201
  });
2200
- }
2201
- else {
2202
- updateValidAndValue(name, true, options.value);
2203
- }
2204
- return {
2205
- ...(disabledIsDefined
2206
- ? { disabled: options.disabled || _options.disabled }
2207
- : {}),
2208
- ...(_options.progressive
2209
- ? {
2210
- required: !!options.required,
2211
- min: getRuleValue(options.min),
2212
- max: getRuleValue(options.max),
2213
- minLength: getRuleValue(options.minLength),
2214
- maxLength: getRuleValue(options.maxLength),
2215
- pattern: getRuleValue(options.pattern),
2216
- }
2217
- : {}),
2218
- name,
2219
- onChange,
2220
- onBlur: onChange,
2221
- ref: (ref) => {
2222
- if (ref) {
2223
- register(name, options);
2224
- field = get(_fields, name);
2225
- const fieldRef = isUndefined(ref.value)
2226
- ? ref.querySelectorAll
2227
- ? ref.querySelectorAll('input,select,textarea')[0] || ref
2228
- : ref
2229
- : ref;
2230
- const radioOrCheckbox = isRadioOrCheckbox(fieldRef);
2231
- const refs = field._f.refs || [];
2232
- if (radioOrCheckbox
2233
- ? refs.find((option) => option === fieldRef)
2234
- : fieldRef === field._f.ref) {
2235
- return;
2236
- }
2237
- set(_fields, name, {
2238
- _f: {
2239
- ...field._f,
2240
- ...(radioOrCheckbox
2241
- ? {
2242
- refs: [
2243
- ...refs.filter(live),
2244
- fieldRef,
2245
- ...(Array.isArray(get(_defaultValues, name)) ? [{}] : []),
2246
- ],
2247
- ref: { type: fieldRef.type, name },
2248
- }
2249
- : { ref: fieldRef }),
2250
- },
2251
- });
2252
- updateValidAndValue(name, false, undefined, fieldRef);
2253
- }
2254
- else {
2255
- field = get(_fields, name, {});
2256
- if (field._f) {
2257
- field._f.mount = false;
2258
- }
2259
- (_options.shouldUnregister || options.shouldUnregister) &&
2260
- !(isNameInFieldArray(_names.array, name) && _state.action) &&
2261
- _names.unMount.add(name);
2262
- }
2202
+ });
2203
+ const methods = {
2204
+ control: {
2205
+ register,
2206
+ unregister,
2207
+ getFieldState,
2208
+ handleSubmit,
2209
+ setError,
2210
+ _subscribe,
2211
+ _runSchema,
2212
+ _getWatch,
2213
+ _getDirty,
2214
+ _setValid,
2215
+ _setFieldArray,
2216
+ _setDisabledField,
2217
+ _setErrors,
2218
+ _getFieldArray,
2219
+ _reset,
2220
+ _resetDefaultValues,
2221
+ _removeUnmounted,
2222
+ _disableForm,
2223
+ _subjects,
2224
+ _proxyFormState,
2225
+ get _fields() {
2226
+ return _fields;
2263
2227
  },
2264
- };
2228
+ get _formValues() {
2229
+ return _formValues;
2230
+ },
2231
+ get _state() {
2232
+ return _state;
2233
+ },
2234
+ set _state(value) {
2235
+ _state = value;
2236
+ },
2237
+ get _defaultValues() {
2238
+ return _defaultValues;
2239
+ },
2240
+ get _names() {
2241
+ return _names;
2242
+ },
2243
+ set _names(value) {
2244
+ _names = value;
2245
+ },
2246
+ get _formState() {
2247
+ return _formState;
2248
+ },
2249
+ get _options() {
2250
+ return _options;
2251
+ },
2252
+ set _options(value) {
2253
+ _options = {
2254
+ ..._options,
2255
+ ...value,
2256
+ };
2257
+ },
2258
+ },
2259
+ subscribe,
2260
+ trigger,
2261
+ register,
2262
+ handleSubmit,
2263
+ watch,
2264
+ setValue,
2265
+ getValues,
2266
+ reset,
2267
+ resetField,
2268
+ clearErrors,
2269
+ unregister,
2270
+ setError,
2271
+ setFocus,
2272
+ getFieldState,
2265
2273
  };
2266
- const _focusError = () => _options.shouldFocusError &&
2267
- iterateFieldsByAction(_fields, _focusInput, _names.mount);
2268
- const _disableForm = (disabled) => {
2269
- if (isBoolean(disabled)) {
2270
- _subjects.state.next({ disabled });
2271
- iterateFieldsByAction(_fields, (ref, name) => {
2272
- const currentField = get(_fields, name);
2273
- if (currentField) {
2274
- ref.disabled = currentField._f.disabled || disabled;
2275
- if (Array.isArray(currentField._f.refs)) {
2276
- currentField._f.refs.forEach((inputRef) => {
2277
- inputRef.disabled = currentField._f.disabled || disabled;
2278
- });
2279
- }
2280
- }
2281
- }, 0, false);
2282
- }
2274
+ return {
2275
+ ...methods,
2276
+ formControl: methods,
2283
2277
  };
2284
- const handleSubmit = (onValid, onInvalid) => async (e) => {
2285
- let onValidError = undefined;
2286
- if (e) {
2287
- e.preventDefault && e.preventDefault();
2288
- e.persist && e.persist();
2289
- }
2290
- if (_options.disabled) {
2291
- if (onInvalid) {
2292
- await onInvalid({ ..._formState.errors }, e);
2278
+ }
2279
+
2280
+ var generateId = () => {
2281
+ const d = typeof performance === 'undefined' ? Date.now() : performance.now() * 1000;
2282
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
2283
+ const r = (Math.random() * 16 + d) % 16 | 0;
2284
+ return (c == 'x' ? r : (r & 0x3) | 0x8).toString(16);
2285
+ });
2286
+ };
2287
+
2288
+ var getFocusFieldName = (name, index, options = {}) => options.shouldFocus || isUndefined(options.shouldFocus)
2289
+ ? options.focusName ||
2290
+ `${name}.${isUndefined(options.focusIndex) ? index : options.focusIndex}.`
2291
+ : '';
2292
+
2293
+ var appendAt = (data, value) => [
2294
+ ...data,
2295
+ ...convertToArrayPayload(value),
2296
+ ];
2297
+
2298
+ var fillEmptyArray = (value) => Array.isArray(value) ? value.map(() => undefined) : undefined;
2299
+
2300
+ function insert(data, index, value) {
2301
+ return [
2302
+ ...data.slice(0, index),
2303
+ ...convertToArrayPayload(value),
2304
+ ...data.slice(index),
2305
+ ];
2306
+ }
2307
+
2308
+ var moveArrayAt = (data, from, to) => {
2309
+ if (!Array.isArray(data)) {
2310
+ return [];
2311
+ }
2312
+ if (isUndefined(data[to])) {
2313
+ data[to] = undefined;
2314
+ }
2315
+ data.splice(to, 0, data.splice(from, 1)[0]);
2316
+ return data;
2317
+ };
2318
+
2319
+ var prependAt = (data, value) => [
2320
+ ...convertToArrayPayload(value),
2321
+ ...convertToArrayPayload(data),
2322
+ ];
2323
+
2324
+ function removeAtIndexes(data, indexes) {
2325
+ let i = 0;
2326
+ const temp = [...data];
2327
+ for (const index of indexes) {
2328
+ temp.splice(index - i, 1);
2329
+ i++;
2330
+ }
2331
+ return compact(temp).length ? temp : [];
2332
+ }
2333
+ var removeArrayAt = (data, index) => isUndefined(index)
2334
+ ? []
2335
+ : removeAtIndexes(data, convertToArrayPayload(index).sort((a, b) => a - b));
2336
+
2337
+ var swapArrayAt = (data, indexA, indexB) => {
2338
+ [data[indexA], data[indexB]] = [data[indexB], data[indexA]];
2339
+ };
2340
+
2341
+ var updateAt = (fieldValues, index, value) => {
2342
+ fieldValues[index] = value;
2343
+ return fieldValues;
2344
+ };
2345
+
2346
+ /**
2347
+ * A custom hook that exposes convenient methods to perform operations with a list of dynamic inputs that need to be appended, updated, removed etc. • [Demo](https://codesandbox.io/s/react-hook-form-usefieldarray-ssugn) • [Video](https://youtu.be/4MrbfGSFY2A)
2348
+ *
2349
+ * @remarks
2350
+ * [API](https://react-hook-form.com/docs/usefieldarray) • [Demo](https://codesandbox.io/s/react-hook-form-usefieldarray-ssugn)
2351
+ *
2352
+ * @param props - useFieldArray props
2353
+ *
2354
+ * @returns methods - functions to manipulate with the Field Arrays (dynamic inputs) {@link UseFieldArrayReturn}
2355
+ *
2356
+ * @example
2357
+ * ```tsx
2358
+ * function App() {
2359
+ * const { register, control, handleSubmit, reset, trigger, setError } = useForm({
2360
+ * defaultValues: {
2361
+ * test: []
2362
+ * }
2363
+ * });
2364
+ * const { fields, append } = useFieldArray({
2365
+ * control,
2366
+ * name: "test"
2367
+ * });
2368
+ *
2369
+ * return (
2370
+ * <form onSubmit={handleSubmit(data => console.log(data))}>
2371
+ * {fields.map((item, index) => (
2372
+ * <input key={item.id} {...register(`test.${index}.firstName`)} />
2373
+ * ))}
2374
+ * <button type="button" onClick={() => append({ firstName: "bill" })}>
2375
+ * append
2376
+ * </button>
2377
+ * <input type="submit" />
2378
+ * </form>
2379
+ * );
2380
+ * }
2381
+ * ```
2382
+ */
2383
+ function useFieldArray(props) {
2384
+ const methods = useFormContext();
2385
+ const { control = methods.control, name, keyName = 'id', shouldUnregister, rules, } = props;
2386
+ const [fields, setFields] = React.useState(control._getFieldArray(name));
2387
+ const ids = React.useRef(control._getFieldArray(name).map(generateId));
2388
+ const _fieldIds = React.useRef(fields);
2389
+ const _name = React.useRef(name);
2390
+ const _actioned = React.useRef(false);
2391
+ _name.current = name;
2392
+ _fieldIds.current = fields;
2393
+ control._names.array.add(name);
2394
+ rules &&
2395
+ control.register(name, rules);
2396
+ React.useEffect(() => control._subjects.array.subscribe({
2397
+ next: ({ values, name: fieldArrayName, }) => {
2398
+ if (fieldArrayName === _name.current || !fieldArrayName) {
2399
+ const fieldValues = get(values, _name.current);
2400
+ if (Array.isArray(fieldValues)) {
2401
+ setFields(fieldValues);
2402
+ ids.current = fieldValues.map(generateId);
2403
+ }
2293
2404
  }
2294
- return;
2295
- }
2296
- let fieldValues = cloneObject(_formValues);
2297
- _subjects.state.next({
2298
- isSubmitting: true,
2405
+ },
2406
+ }).unsubscribe, [control]);
2407
+ const updateValues = React.useCallback((updatedFieldArrayValues) => {
2408
+ _actioned.current = true;
2409
+ control._setFieldArray(name, updatedFieldArrayValues);
2410
+ }, [control, name]);
2411
+ const append = (value, options) => {
2412
+ const appendValue = convertToArrayPayload(cloneObject(value));
2413
+ const updatedFieldArrayValues = appendAt(control._getFieldArray(name), appendValue);
2414
+ control._names.focus = getFocusFieldName(name, updatedFieldArrayValues.length - 1, options);
2415
+ ids.current = appendAt(ids.current, appendValue.map(generateId));
2416
+ updateValues(updatedFieldArrayValues);
2417
+ setFields(updatedFieldArrayValues);
2418
+ control._setFieldArray(name, updatedFieldArrayValues, appendAt, {
2419
+ argA: fillEmptyArray(value),
2299
2420
  });
2300
- if (_options.resolver) {
2301
- const { errors, values } = await _executeSchema();
2302
- _formState.errors = errors;
2303
- fieldValues = values;
2304
- }
2305
- else {
2306
- await executeBuiltInValidation(_fields);
2307
- }
2308
- unset(_formState.errors, 'root');
2309
- if (isEmptyObject(_formState.errors)) {
2310
- _subjects.state.next({
2311
- errors: {},
2312
- });
2313
- try {
2314
- await onValid(fieldValues, e);
2315
- }
2316
- catch (error) {
2317
- onValidError = error;
2318
- }
2319
- }
2320
- else {
2321
- if (onInvalid) {
2322
- await onInvalid({ ..._formState.errors }, e);
2323
- }
2324
- _focusError();
2325
- setTimeout(_focusError);
2326
- }
2327
- _subjects.state.next({
2328
- isSubmitted: true,
2329
- isSubmitting: false,
2330
- isSubmitSuccessful: isEmptyObject(_formState.errors) && !onValidError,
2331
- submitCount: _formState.submitCount + 1,
2332
- errors: _formState.errors,
2421
+ };
2422
+ const prepend = (value, options) => {
2423
+ const prependValue = convertToArrayPayload(cloneObject(value));
2424
+ const updatedFieldArrayValues = prependAt(control._getFieldArray(name), prependValue);
2425
+ control._names.focus = getFocusFieldName(name, 0, options);
2426
+ ids.current = prependAt(ids.current, prependValue.map(generateId));
2427
+ updateValues(updatedFieldArrayValues);
2428
+ setFields(updatedFieldArrayValues);
2429
+ control._setFieldArray(name, updatedFieldArrayValues, prependAt, {
2430
+ argA: fillEmptyArray(value),
2333
2431
  });
2334
- if (onValidError) {
2335
- throw onValidError;
2336
- }
2337
2432
  };
2338
- const resetField = (name, options = {}) => {
2339
- if (get(_fields, name)) {
2340
- if (isUndefined(options.defaultValue)) {
2341
- setValue(name, cloneObject(get(_defaultValues, name)));
2342
- }
2343
- else {
2344
- setValue(name, options.defaultValue);
2345
- set(_defaultValues, name, cloneObject(options.defaultValue));
2346
- }
2347
- if (!options.keepTouched) {
2348
- unset(_formState.touchedFields, name);
2349
- }
2350
- if (!options.keepDirty) {
2351
- unset(_formState.dirtyFields, name);
2352
- _formState.isDirty = options.defaultValue
2353
- ? _getDirty(name, cloneObject(get(_defaultValues, name)))
2354
- : _getDirty();
2355
- }
2356
- if (!options.keepError) {
2357
- unset(_formState.errors, name);
2358
- _proxyFormState.isValid && _updateValid();
2359
- }
2360
- _subjects.state.next({ ..._formState });
2361
- }
2433
+ const remove = (index) => {
2434
+ const updatedFieldArrayValues = removeArrayAt(control._getFieldArray(name), index);
2435
+ ids.current = removeArrayAt(ids.current, index);
2436
+ updateValues(updatedFieldArrayValues);
2437
+ setFields(updatedFieldArrayValues);
2438
+ !Array.isArray(get(control._fields, name)) &&
2439
+ set(control._fields, name, undefined);
2440
+ control._setFieldArray(name, updatedFieldArrayValues, removeArrayAt, {
2441
+ argA: index,
2442
+ });
2443
+ };
2444
+ const insert$1 = (index, value, options) => {
2445
+ const insertValue = convertToArrayPayload(cloneObject(value));
2446
+ const updatedFieldArrayValues = insert(control._getFieldArray(name), index, insertValue);
2447
+ control._names.focus = getFocusFieldName(name, index, options);
2448
+ ids.current = insert(ids.current, index, insertValue.map(generateId));
2449
+ updateValues(updatedFieldArrayValues);
2450
+ setFields(updatedFieldArrayValues);
2451
+ control._setFieldArray(name, updatedFieldArrayValues, insert, {
2452
+ argA: index,
2453
+ argB: fillEmptyArray(value),
2454
+ });
2455
+ };
2456
+ const swap = (indexA, indexB) => {
2457
+ const updatedFieldArrayValues = control._getFieldArray(name);
2458
+ swapArrayAt(updatedFieldArrayValues, indexA, indexB);
2459
+ swapArrayAt(ids.current, indexA, indexB);
2460
+ updateValues(updatedFieldArrayValues);
2461
+ setFields(updatedFieldArrayValues);
2462
+ control._setFieldArray(name, updatedFieldArrayValues, swapArrayAt, {
2463
+ argA: indexA,
2464
+ argB: indexB,
2465
+ }, false);
2466
+ };
2467
+ const move = (from, to) => {
2468
+ const updatedFieldArrayValues = control._getFieldArray(name);
2469
+ moveArrayAt(updatedFieldArrayValues, from, to);
2470
+ moveArrayAt(ids.current, from, to);
2471
+ updateValues(updatedFieldArrayValues);
2472
+ setFields(updatedFieldArrayValues);
2473
+ control._setFieldArray(name, updatedFieldArrayValues, moveArrayAt, {
2474
+ argA: from,
2475
+ argB: to,
2476
+ }, false);
2477
+ };
2478
+ const update = (index, value) => {
2479
+ const updateValue = cloneObject(value);
2480
+ const updatedFieldArrayValues = updateAt(control._getFieldArray(name), index, updateValue);
2481
+ ids.current = [...updatedFieldArrayValues].map((item, i) => !item || i === index ? generateId() : ids.current[i]);
2482
+ updateValues(updatedFieldArrayValues);
2483
+ setFields([...updatedFieldArrayValues]);
2484
+ control._setFieldArray(name, updatedFieldArrayValues, updateAt, {
2485
+ argA: index,
2486
+ argB: updateValue,
2487
+ }, true, false);
2488
+ };
2489
+ const replace = (value) => {
2490
+ const updatedFieldArrayValues = convertToArrayPayload(cloneObject(value));
2491
+ ids.current = updatedFieldArrayValues.map(generateId);
2492
+ updateValues([...updatedFieldArrayValues]);
2493
+ setFields([...updatedFieldArrayValues]);
2494
+ control._setFieldArray(name, [...updatedFieldArrayValues], (data) => data, {}, true, false);
2362
2495
  };
2363
- const _reset = (formValues, keepStateOptions = {}) => {
2364
- const updatedValues = formValues ? cloneObject(formValues) : _defaultValues;
2365
- const cloneUpdatedValues = cloneObject(updatedValues);
2366
- const isEmptyResetValues = isEmptyObject(formValues);
2367
- const values = isEmptyResetValues ? _defaultValues : cloneUpdatedValues;
2368
- if (!keepStateOptions.keepDefaultValues) {
2369
- _defaultValues = updatedValues;
2370
- }
2371
- if (!keepStateOptions.keepValues) {
2372
- if (keepStateOptions.keepDirtyValues) {
2373
- const fieldsToCheck = new Set([
2374
- ..._names.mount,
2375
- ...Object.keys(getDirtyFields(_defaultValues, _formValues)),
2376
- ]);
2377
- for (const fieldName of Array.from(fieldsToCheck)) {
2378
- get(_formState.dirtyFields, fieldName)
2379
- ? set(values, fieldName, get(_formValues, fieldName))
2380
- : setValue(fieldName, get(values, fieldName));
2381
- }
2496
+ React.useEffect(() => {
2497
+ control._state.action = false;
2498
+ isWatched(name, control._names) &&
2499
+ control._subjects.state.next({
2500
+ ...control._formState,
2501
+ });
2502
+ if (_actioned.current &&
2503
+ (!getValidationModes(control._options.mode).isOnSubmit ||
2504
+ control._formState.isSubmitted)) {
2505
+ if (control._options.resolver) {
2506
+ control._runSchema([name]).then((result) => {
2507
+ const error = get(result.errors, name);
2508
+ const existingError = get(control._formState.errors, name);
2509
+ if (existingError
2510
+ ? (!error && existingError.type) ||
2511
+ (error &&
2512
+ (existingError.type !== error.type ||
2513
+ existingError.message !== error.message))
2514
+ : error && error.type) {
2515
+ error
2516
+ ? set(control._formState.errors, name, error)
2517
+ : unset(control._formState.errors, name);
2518
+ control._subjects.state.next({
2519
+ errors: control._formState.errors,
2520
+ });
2521
+ }
2522
+ });
2382
2523
  }
2383
2524
  else {
2384
- if (isWeb && isUndefined(formValues)) {
2385
- for (const name of _names.mount) {
2386
- const field = get(_fields, name);
2387
- if (field && field._f) {
2388
- const fieldReference = Array.isArray(field._f.refs)
2389
- ? field._f.refs[0]
2390
- : field._f.ref;
2391
- if (isHTMLElement(fieldReference)) {
2392
- const form = fieldReference.closest('form');
2393
- if (form) {
2394
- form.reset();
2395
- break;
2396
- }
2397
- }
2398
- }
2399
- }
2525
+ const field = get(control._fields, name);
2526
+ if (field &&
2527
+ field._f &&
2528
+ !(getValidationModes(control._options.reValidateMode).isOnSubmit &&
2529
+ getValidationModes(control._options.mode).isOnSubmit)) {
2530
+ validateField(field, control._names.disabled, control._formValues, control._options.criteriaMode === VALIDATION_MODE.all, control._options.shouldUseNativeValidation, true).then((error) => !isEmptyObject(error) &&
2531
+ control._subjects.state.next({
2532
+ errors: updateFieldArrayRootError(control._formState.errors, error, name),
2533
+ }));
2400
2534
  }
2401
- _fields = {};
2402
2535
  }
2403
- _formValues = _options.shouldUnregister
2404
- ? keepStateOptions.keepDefaultValues
2405
- ? cloneObject(_defaultValues)
2406
- : {}
2407
- : cloneObject(values);
2408
- _subjects.array.next({
2409
- values: { ...values },
2410
- });
2411
- _subjects.values.next({
2412
- values: { ...values },
2413
- });
2414
2536
  }
2415
- _names = {
2416
- mount: keepStateOptions.keepDirtyValues ? _names.mount : new Set(),
2417
- unMount: new Set(),
2418
- array: new Set(),
2419
- watch: new Set(),
2420
- watchAll: false,
2421
- focus: '',
2422
- };
2423
- _state.mount =
2424
- !_proxyFormState.isValid ||
2425
- !!keepStateOptions.keepIsValid ||
2426
- !!keepStateOptions.keepDirtyValues;
2427
- _state.watch = !!_options.shouldUnregister;
2428
- _subjects.state.next({
2429
- submitCount: keepStateOptions.keepSubmitCount
2430
- ? _formState.submitCount
2431
- : 0,
2432
- isDirty: isEmptyResetValues
2433
- ? false
2434
- : keepStateOptions.keepDirty
2435
- ? _formState.isDirty
2436
- : !!(keepStateOptions.keepDefaultValues &&
2437
- !deepEqual(formValues, _defaultValues)),
2438
- isSubmitted: keepStateOptions.keepIsSubmitted
2439
- ? _formState.isSubmitted
2440
- : false,
2441
- dirtyFields: isEmptyResetValues
2442
- ? {}
2443
- : keepStateOptions.keepDirtyValues
2444
- ? keepStateOptions.keepDefaultValues && _formValues
2445
- ? getDirtyFields(_defaultValues, _formValues)
2446
- : _formState.dirtyFields
2447
- : keepStateOptions.keepDefaultValues && formValues
2448
- ? getDirtyFields(_defaultValues, formValues)
2449
- : keepStateOptions.keepDirty
2450
- ? _formState.dirtyFields
2451
- : {},
2452
- touchedFields: keepStateOptions.keepTouched
2453
- ? _formState.touchedFields
2454
- : {},
2455
- errors: keepStateOptions.keepErrors ? _formState.errors : {},
2456
- isSubmitSuccessful: keepStateOptions.keepIsSubmitSuccessful
2457
- ? _formState.isSubmitSuccessful
2458
- : false,
2459
- isSubmitting: false,
2537
+ control._subjects.state.next({
2538
+ name,
2539
+ values: { ...control._formValues },
2460
2540
  });
2461
- };
2462
- const reset = (formValues, keepStateOptions) => _reset(isFunction(formValues)
2463
- ? formValues(_formValues)
2464
- : formValues, keepStateOptions);
2465
- const setFocus = (name, options = {}) => {
2466
- const field = get(_fields, name);
2467
- const fieldReference = field && field._f;
2468
- if (fieldReference) {
2469
- const fieldRef = fieldReference.refs
2470
- ? fieldReference.refs[0]
2471
- : fieldReference.ref;
2472
- if (fieldRef.focus) {
2473
- fieldRef.focus();
2474
- options.shouldSelect &&
2475
- isFunction(fieldRef.select) &&
2476
- fieldRef.select();
2477
- }
2478
- }
2479
- };
2480
- const _updateFormState = (updatedFormState) => {
2481
- _formState = {
2482
- ..._formState,
2483
- ...updatedFormState,
2484
- };
2485
- };
2486
- const _resetDefaultValues = () => isFunction(_options.defaultValues) &&
2487
- _options.defaultValues().then((values) => {
2488
- reset(values, _options.resetOptions);
2489
- _subjects.state.next({
2490
- isLoading: false,
2541
+ control._names.focus &&
2542
+ iterateFieldsByAction(control._fields, (ref, key) => {
2543
+ if (control._names.focus &&
2544
+ key.startsWith(control._names.focus) &&
2545
+ ref.focus) {
2546
+ ref.focus();
2547
+ return 1;
2548
+ }
2549
+ return;
2491
2550
  });
2492
- });
2551
+ control._names.focus = '';
2552
+ control._setValid();
2553
+ _actioned.current = false;
2554
+ }, [fields, name, control]);
2555
+ React.useEffect(() => {
2556
+ !get(control._formValues, name) && control._setFieldArray(name);
2557
+ return () => {
2558
+ (control._options.shouldUnregister || shouldUnregister) &&
2559
+ control.unregister(name);
2560
+ };
2561
+ }, [name, control, keyName, shouldUnregister]);
2493
2562
  return {
2494
- control: {
2495
- register,
2496
- unregister,
2497
- getFieldState,
2498
- handleSubmit,
2499
- setError,
2500
- _executeSchema,
2501
- _getWatch,
2502
- _getDirty,
2503
- _updateValid,
2504
- _removeUnmounted,
2505
- _updateFieldArray,
2506
- _updateDisabledField,
2507
- _getFieldArray,
2508
- _reset,
2509
- _resetDefaultValues,
2510
- _updateFormState,
2511
- _disableForm,
2512
- _subjects,
2513
- _proxyFormState,
2514
- _setErrors,
2515
- get _fields() {
2516
- return _fields;
2517
- },
2518
- get _formValues() {
2519
- return _formValues;
2520
- },
2521
- get _state() {
2522
- return _state;
2523
- },
2524
- set _state(value) {
2525
- _state = value;
2526
- },
2527
- get _defaultValues() {
2528
- return _defaultValues;
2529
- },
2530
- get _names() {
2531
- return _names;
2532
- },
2533
- set _names(value) {
2534
- _names = value;
2535
- },
2536
- get _formState() {
2537
- return _formState;
2538
- },
2539
- set _formState(value) {
2540
- _formState = value;
2541
- },
2542
- get _options() {
2543
- return _options;
2544
- },
2545
- set _options(value) {
2546
- _options = {
2547
- ..._options,
2548
- ...value,
2549
- };
2550
- },
2551
- },
2552
- trigger,
2553
- register,
2554
- handleSubmit,
2555
- watch,
2556
- setValue,
2557
- getValues,
2558
- reset,
2559
- resetField,
2560
- clearErrors,
2561
- unregister,
2562
- setError,
2563
- setFocus,
2564
- getFieldState,
2563
+ swap: React.useCallback(swap, [updateValues, name, control]),
2564
+ move: React.useCallback(move, [updateValues, name, control]),
2565
+ prepend: React.useCallback(prepend, [updateValues, name, control]),
2566
+ append: React.useCallback(append, [updateValues, name, control]),
2567
+ remove: React.useCallback(remove, [updateValues, name, control]),
2568
+ insert: React.useCallback(insert$1, [updateValues, name, control]),
2569
+ update: React.useCallback(update, [updateValues, name, control]),
2570
+ replace: React.useCallback(replace, [updateValues, name, control]),
2571
+ fields: React.useMemo(() => fields.map((field, index) => ({
2572
+ ...field,
2573
+ [keyName]: ids.current[index] || generateId(),
2574
+ })), [fields, keyName]),
2565
2575
  };
2566
2576
  }
2567
2577
 
@@ -2617,20 +2627,17 @@ function useForm(props = {}) {
2617
2627
  });
2618
2628
  if (!_formControl.current) {
2619
2629
  _formControl.current = {
2620
- ...createFormControl(props),
2630
+ ...(props.control ? props.control : createFormControl(props)),
2621
2631
  formState,
2622
2632
  };
2623
2633
  }
2624
2634
  const control = _formControl.current.control;
2625
2635
  control._options = props;
2626
- useSubscribe({
2627
- subject: control._subjects.state,
2628
- next: (value) => {
2629
- if (shouldRenderFormState(value, control._proxyFormState, control._updateFormState, true)) {
2630
- updateFormState({ ...control._formState });
2631
- }
2632
- },
2633
- });
2636
+ React.useEffect(() => control._subscribe({
2637
+ formState: control._proxyFormState,
2638
+ callback: () => updateFormState({ ...control._formState }),
2639
+ reRenderRoot: true,
2640
+ }), [control]);
2634
2641
  React.useEffect(() => control._disableForm(props.disabled), [control, props.disabled]);
2635
2642
  React.useEffect(() => {
2636
2643
  if (control._proxyFormState.isDirty) {
@@ -2659,7 +2666,7 @@ function useForm(props = {}) {
2659
2666
  }, [props.errors, control]);
2660
2667
  React.useEffect(() => {
2661
2668
  if (!control._state.mount) {
2662
- control._updateValid();
2669
+ control._setValid();
2663
2670
  control._state.mount = true;
2664
2671
  }
2665
2672
  if (control._state.watch) {
@@ -2670,7 +2677,7 @@ function useForm(props = {}) {
2670
2677
  });
2671
2678
  React.useEffect(() => {
2672
2679
  props.shouldUnregister &&
2673
- control._subjects.values.next({
2680
+ control._subjects.state.next({
2674
2681
  values: control._getWatch(),
2675
2682
  });
2676
2683
  }, [props.shouldUnregister, control]);
@@ -2678,5 +2685,5 @@ function useForm(props = {}) {
2678
2685
  return _formControl.current;
2679
2686
  }
2680
2687
 
2681
- export { Controller, Form, FormProvider, appendErrors, get, set, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch };
2688
+ export { Controller, Form, FormProvider, appendErrors, createFormControl, get, set, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch };
2682
2689
  //# sourceMappingURL=index.esm.mjs.map