react-hook-form 7.88.0 → 7.89.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.
@@ -97,6 +97,16 @@ const INPUT_VALIDATION_RULES = {
97
97
  required: 'required',
98
98
  validate: 'validate',
99
99
  };
100
+ const REGISTER_VALIDATION_RULES = [
101
+ INPUT_VALIDATION_RULES.required,
102
+ INPUT_VALIDATION_RULES.min,
103
+ INPUT_VALIDATION_RULES.max,
104
+ INPUT_VALIDATION_RULES.minLength,
105
+ INPUT_VALIDATION_RULES.maxLength,
106
+ INPUT_VALIDATION_RULES.pattern,
107
+ INPUT_VALIDATION_RULES.validate,
108
+ ];
109
+ const FORM_ERROR_TYPE = 'form';
100
110
  const ROOT_ERROR_TYPE = 'root';
101
111
  const PROTOTYPE_KEYWORDS = ['__proto__', 'constructor', 'prototype'];
102
112
 
@@ -541,6 +551,7 @@ function useController(props) {
541
551
  const field = get(control._fields, name);
542
552
  if (field && field._f && elm) {
543
553
  field._f.ref = _proxyRef.current;
554
+ field._f._c = true;
544
555
  }
545
556
  }, [control._fields, name]);
546
557
  const field = React.useMemo(() => ({
@@ -565,6 +576,7 @@ function useController(props) {
565
576
  const field = get(control._fields, name);
566
577
  if (field && field._f) {
567
578
  field._f.mount = value;
579
+ field._f._c = value;
568
580
  }
569
581
  };
570
582
  updateMounted(name, true);
@@ -577,7 +589,13 @@ function useController(props) {
577
589
  set(control._formValues, name, value);
578
590
  }
579
591
  }
580
- !isArrayField && control.register(name);
592
+ !isArrayField &&
593
+ control.register(name, {
594
+ ..._props.current.rules,
595
+ ...(isBoolean(_props.current.disabled)
596
+ ? { disabled: _props.current.disabled }
597
+ : {}),
598
+ });
581
599
  if (_proxyRef.current) {
582
600
  const field = get(control._fields, name);
583
601
  if (field && field._f) {
@@ -807,7 +825,7 @@ var getValueAndMessage = (validationData) => isObject(validationData) && !isRege
807
825
  };
808
826
 
809
827
  var validateField = async (field, disabledFieldNames, formValues, validateAllFieldCriteria, shouldUseNativeValidation, isFieldArray) => {
810
- const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, } = field._f;
828
+ const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, _c, } = field._f;
811
829
  const inputValue = get(formValues, name);
812
830
  if (!mount || disabledFieldNames.has(name)) {
813
831
  return {};
@@ -817,7 +835,8 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
817
835
  if (shouldUseNativeValidation && inputRef.reportValidity) {
818
836
  const validityMessage = isBoolean(message) ? '' : message || '';
819
837
  if (refs) {
820
- refs.forEach((ref) => ref.setCustomValidity(validityMessage));
838
+ refs.forEach((ref) => isFunction(ref.setCustomValidity) &&
839
+ ref.setCustomValidity(validityMessage));
821
840
  }
822
841
  else {
823
842
  inputRef.setCustomValidity(validityMessage);
@@ -832,7 +851,7 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
832
851
  const isEmpty = ((valueAsNumber || isFileInput(ref)) &&
833
852
  isUndefined(ref.value) &&
834
853
  isUndefined(inputValue)) ||
835
- (isHTMLElement(ref) && ref.value === '') ||
854
+ (isHTMLElement(ref) && ref.value === '' && !_c) ||
836
855
  inputValue === '' ||
837
856
  (Array.isArray(inputValue) && !inputValue.length);
838
857
  const appendErrorsCurry = appendErrors.bind(null, name, validateAllFieldCriteria, error);
@@ -1334,10 +1353,23 @@ function useFieldArray(props) {
1334
1353
  else {
1335
1354
  const field = get(control._fields, name);
1336
1355
  if (field && field._f) {
1337
- validateField(field, control._names.disabled, control._formValues, control._options.criteriaMode === VALIDATION_MODE.all, control._options.shouldUseNativeValidation, true).then((error) => !isEmptyObject(error) &&
1338
- control._subjects.state.next({
1339
- errors: updateFieldArrayRootError(control._formState.errors, error, name),
1340
- }));
1356
+ validateField(field, control._names.disabled, control._formValues, control._options.criteriaMode === VALIDATION_MODE.all, control._options.shouldUseNativeValidation, true).then((error) => {
1357
+ if (!isEmptyObject(error)) {
1358
+ control._subjects.state.next({
1359
+ errors: updateFieldArrayRootError(control._formState.errors, error, name),
1360
+ });
1361
+ }
1362
+ else {
1363
+ const existingError = get(control._formState.errors, name);
1364
+ if (existingError && existingError[ROOT_ERROR_TYPE]) {
1365
+ unset(control._formState.errors, `${name}.${ROOT_ERROR_TYPE}`);
1366
+ control._subjects.state.next({
1367
+ errors: control._formState
1368
+ .errors,
1369
+ });
1370
+ }
1371
+ }
1372
+ });
1341
1373
  }
1342
1374
  }
1343
1375
  }
@@ -1365,7 +1397,8 @@ function useFieldArray(props) {
1365
1397
  }, [fields, name, control, disabled]);
1366
1398
  React.useEffect(() => {
1367
1399
  if (!disabled) {
1368
- !get(control._formValues, name) && control._setFieldArray(name);
1400
+ isUndefined(get(control._formValues, name)) &&
1401
+ control._setFieldArray(name);
1369
1402
  }
1370
1403
  return () => {
1371
1404
  control._state.actionArrayLengths.delete(name);
@@ -1718,16 +1751,15 @@ var createSubject = () => {
1718
1751
  };
1719
1752
 
1720
1753
  function extractFormValues(fieldsState, formValues) {
1721
- const values = {};
1754
+ const values = (Array.isArray(fieldsState) ? [] : {});
1722
1755
  for (const key in fieldsState) {
1723
1756
  if (fieldsState.hasOwnProperty(key)) {
1724
1757
  const fieldState = fieldsState[key];
1725
1758
  const fieldValue = formValues[key];
1726
- if (fieldState && isObject(fieldState) && fieldValue) {
1727
- const nestedFieldsState = extractFormValues(fieldState, fieldValue);
1728
- if (isObject(nestedFieldsState)) {
1729
- values[key] = nestedFieldsState;
1730
- }
1759
+ if (fieldState &&
1760
+ (isObject(fieldState) || Array.isArray(fieldState)) &&
1761
+ fieldValue) {
1762
+ values[key] = extractFormValues(fieldState, fieldValue);
1731
1763
  }
1732
1764
  else if (fieldsState[key]) {
1733
1765
  values[key] = fieldValue;
@@ -2030,7 +2062,6 @@ const defaultOptions = {
2030
2062
  reValidateMode: VALIDATION_MODE.onChange,
2031
2063
  shouldFocusError: true,
2032
2064
  };
2033
- const FORM_ERROR_TYPE = 'form';
2034
2065
  const updateDirtyFields = (dirtyFields, nextDirtyFields) => {
2035
2066
  for (const key in dirtyFields) {
2036
2067
  if (!(key in nextDirtyFields)) {
@@ -2194,12 +2225,16 @@ function createFormControl(props = {}) {
2194
2225
  unsetEmptyArray(_formState.errors, name);
2195
2226
  }
2196
2227
  const touchedFieldsArray = get(_formState.touchedFields, name);
2197
- if (_isTracked('touchedFields') &&
2198
- shouldUpdateFieldsAndState &&
2199
- Array.isArray(touchedFieldsArray)) {
2228
+ const shouldUpdateTouchedFields = shouldUpdateFieldsAndState && Array.isArray(touchedFieldsArray);
2229
+ if (shouldUpdateTouchedFields) {
2200
2230
  const touchedFields = method(touchedFieldsArray, args.argA, args.argB);
2201
2231
  shouldSetValues && set(_formState.touchedFields, name, touchedFields);
2202
2232
  }
2233
+ const dirtyFieldsArray = get(_formState.dirtyFields, name);
2234
+ if (shouldUpdateFieldsAndState && Array.isArray(dirtyFieldsArray)) {
2235
+ const dirtyFields = method(dirtyFieldsArray, args.argA, args.argB) || dirtyFieldsArray;
2236
+ shouldSetValues && set(_formState.dirtyFields, name, dirtyFields);
2237
+ }
2203
2238
  if (_isTracked('dirtyFields')) {
2204
2239
  _updateDirtyFields();
2205
2240
  }
@@ -2207,6 +2242,9 @@ function createFormControl(props = {}) {
2207
2242
  name,
2208
2243
  isDirty: _getDirty(name, values),
2209
2244
  dirtyFields: _formState.dirtyFields,
2245
+ ...(shouldUpdateTouchedFields && {
2246
+ touchedFields: _formState.touchedFields,
2247
+ }),
2210
2248
  errors: _formState.errors,
2211
2249
  isValid: _formState.isValid,
2212
2250
  });
@@ -2224,11 +2262,13 @@ function createFormControl(props = {}) {
2224
2262
  };
2225
2263
  const _setErrors = (errors) => {
2226
2264
  Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
2265
+ const hasErrors = !isEmptyObject(errors);
2227
2266
  _formState.errors = errors;
2228
2267
  _subjects.state.next({
2229
2268
  errors: _formState.errors,
2230
- isValid: false,
2269
+ ...(hasErrors ? { isValid: false } : {}),
2231
2270
  });
2271
+ !hasErrors && _state.mount && _setValid();
2232
2272
  };
2233
2273
  const hasExplicitNullIntermediate = (name) => {
2234
2274
  const segments = isKey(name) ? [name] : stringToPath(name);
@@ -2296,7 +2336,13 @@ function createFormControl(props = {}) {
2296
2336
  ? set(_formValues, name, shouldSkipSetValueAs ? defaultValue : getFieldValue(field._f))
2297
2337
  : setFieldValue(name, defaultValue);
2298
2338
  if (_state.mount && !_state.action) {
2299
- _setValid();
2339
+ if (_options.resolver &&
2340
+ _isTracked('isValidating', 'validatingFields')) {
2341
+ Promise.resolve().then(() => _setValid());
2342
+ }
2343
+ else {
2344
+ _setValid();
2345
+ }
2300
2346
  if (wasUnsetInFormValues &&
2301
2347
  _formState.isDirty &&
2302
2348
  _isTracked('isDirty')) {
@@ -2404,7 +2450,7 @@ function createFormControl(props = {}) {
2404
2450
  if (names) {
2405
2451
  for (const name of names) {
2406
2452
  const error = get(errors, name);
2407
- cancelDelayedError(name);
2453
+ cancelDelayedErrorTree(name);
2408
2454
  const isFieldArrayRootError = _names.array.has(name) &&
2409
2455
  isObject(error) &&
2410
2456
  !Object.keys(error).some((key) => !Number.isNaN(Number(key)));
@@ -2428,34 +2474,43 @@ function createFormControl(props = {}) {
2428
2474
  return errors;
2429
2475
  };
2430
2476
  const validateForm = async ({ name, eventType, }) => {
2431
- if (props.validate) {
2432
- const result = await props.validate({
2477
+ if (_options.validate) {
2478
+ const resetCallId = _resetCallId;
2479
+ const result = await _options.validate({
2433
2480
  formValues: _formValues,
2434
2481
  formState: _formState,
2435
2482
  name,
2436
2483
  eventType,
2437
2484
  });
2485
+ if (resetCallId !== _resetCallId) {
2486
+ return true;
2487
+ }
2438
2488
  if (isObject(result)) {
2489
+ let isValid = true;
2490
+ clearErrors(FORM_ERROR_TYPE);
2439
2491
  for (const key in result) {
2440
2492
  const error = result[key];
2441
2493
  if (error) {
2494
+ isValid = false;
2442
2495
  setError(`${FORM_ERROR_TYPE}.${key}`, {
2443
2496
  message: isString(error.message) ? error.message : '',
2444
2497
  type: error.type || INPUT_VALIDATION_RULES.validate,
2445
2498
  });
2446
2499
  }
2447
2500
  }
2501
+ return isValid;
2448
2502
  }
2449
2503
  else if (isString(result) || !result) {
2450
2504
  setError(FORM_ERROR_TYPE, {
2451
2505
  message: result || '',
2452
2506
  type: INPUT_VALIDATION_RULES.validate,
2453
2507
  });
2508
+ return false;
2454
2509
  }
2455
2510
  else {
2456
2511
  clearErrors(FORM_ERROR_TYPE);
2512
+ return true;
2457
2513
  }
2458
- return result;
2459
2514
  }
2460
2515
  return true;
2461
2516
  };
@@ -2463,7 +2518,8 @@ function createFormControl(props = {}) {
2463
2518
  valid: true,
2464
2519
  runRootValidation: false,
2465
2520
  }, }) => {
2466
- if (props.validate) {
2521
+ const resetCallId = _resetCallId;
2522
+ if (_options.validate && !context.runRootValidation) {
2467
2523
  context.runRootValidation = true;
2468
2524
  const result = await validateForm({
2469
2525
  name,
@@ -2488,6 +2544,9 @@ function createFormControl(props = {}) {
2488
2544
  _updateIsValidating([_f.name], true);
2489
2545
  }
2490
2546
  const fieldError = await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !onlyCheckValid, isFieldArrayRoot);
2547
+ if (resetCallId !== _resetCallId) {
2548
+ return context.valid;
2549
+ }
2491
2550
  if (isPromiseFunction && shouldTrackIsValidatingState) {
2492
2551
  _updateIsValidating([_f.name]);
2493
2552
  }
@@ -2602,6 +2661,13 @@ function createFormControl(props = {}) {
2602
2661
  trigger(name, {
2603
2662
  delayError: options.delayError,
2604
2663
  });
2664
+ if (options.shouldValidate &&
2665
+ field &&
2666
+ field._f &&
2667
+ field._f.deps &&
2668
+ (!Array.isArray(field._f.deps) || field._f.deps.length > 0)) {
2669
+ trigger(field._f.deps);
2670
+ }
2605
2671
  };
2606
2672
  const setFieldValues = (name, value, options, skipClone = false, skipRender = false, skipValueRender = false) => {
2607
2673
  if (_names.array.has(name)) {
@@ -2649,6 +2715,10 @@ function createFormControl(props = {}) {
2649
2715
  });
2650
2716
  }
2651
2717
  }
2718
+ options.shouldValidate &&
2719
+ trigger(name, {
2720
+ delayError: options.delayError,
2721
+ });
2652
2722
  }
2653
2723
  else {
2654
2724
  const isEmpty = (Array.isArray(cloneValue) && !cloneValue.length) ||
@@ -2722,7 +2792,7 @@ function createFormControl(props = {}) {
2722
2792
  : getEventValue(event);
2723
2793
  const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
2724
2794
  const hasNoValidationEffect = !hasValidation(field._f) &&
2725
- !props.validate &&
2795
+ !_options.validate &&
2726
2796
  !_options.resolver &&
2727
2797
  !get(_formState.errors, name) &&
2728
2798
  !field._f.deps;
@@ -2765,7 +2835,7 @@ function createFormControl(props = {}) {
2765
2835
  return (shouldRender &&
2766
2836
  _subjects.state.next({ name, ...(watched ? {} : fieldState) }));
2767
2837
  }
2768
- if (!_options.resolver && props.validate) {
2838
+ if (!_options.resolver && _options.validate) {
2769
2839
  await validateForm({
2770
2840
  name: name,
2771
2841
  eventType: event.type,
@@ -2773,7 +2843,11 @@ function createFormControl(props = {}) {
2773
2843
  }
2774
2844
  !isBlurEvent && watched && _subjects.state.next({ ..._formState });
2775
2845
  if (_options.resolver) {
2846
+ const resetCallId = _resetCallId;
2776
2847
  const { errors } = await _runSchema([name]);
2848
+ if (resetCallId !== _resetCallId) {
2849
+ return;
2850
+ }
2777
2851
  _updateIsValidating([name]);
2778
2852
  _updateIsFieldValueUpdated(fieldValue);
2779
2853
  if (!isFieldValueUpdated) {
@@ -2787,8 +2861,12 @@ function createFormControl(props = {}) {
2787
2861
  isValid = isEmptyObject(errors);
2788
2862
  }
2789
2863
  else {
2864
+ const resetCallId = _resetCallId;
2790
2865
  _updateIsValidating([name], true);
2791
2866
  error = (await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation))[name];
2867
+ if (resetCallId !== _resetCallId) {
2868
+ return;
2869
+ }
2792
2870
  _updateIsValidating([name]);
2793
2871
  _updateIsFieldValueUpdated(fieldValue);
2794
2872
  if (isFieldValueUpdated) {
@@ -2802,6 +2880,9 @@ function createFormControl(props = {}) {
2802
2880
  name: name,
2803
2881
  eventType: event.type,
2804
2882
  });
2883
+ if (resetCallId !== _resetCallId) {
2884
+ return;
2885
+ }
2805
2886
  }
2806
2887
  }
2807
2888
  }
@@ -2935,7 +3016,7 @@ function createFormControl(props = {}) {
2935
3016
  }
2936
3017
  };
2937
3018
  const setError = (name, error, options) => {
2938
- cancelDelayedError(name);
3019
+ cancelDelayedErrorTree(name);
2939
3020
  const ref = (get(_fields, name, { _f: {} })._f || {}).ref;
2940
3021
  const currentError = get(_formState.errors, name) || {};
2941
3022
  const { ref: currentRef, message, type, types, ...restOfErrorTree } = currentError;
@@ -3078,6 +3159,14 @@ function createFormControl(props = {}) {
3078
3159
  },
3079
3160
  });
3080
3161
  _names.mount.add(name);
3162
+ if (field && field._f) {
3163
+ const nextField = get(_fields, name);
3164
+ for (const rule of REGISTER_VALIDATION_RULES) {
3165
+ if (!(rule in options)) {
3166
+ delete nextField._f[rule];
3167
+ }
3168
+ }
3169
+ }
3081
3170
  if (field && !shouldRevalidateRemount) {
3082
3171
  _setDisabledField({
3083
3172
  disabled: isBoolean(options.disabled)
@@ -3199,10 +3288,14 @@ function createFormControl(props = {}) {
3199
3288
  fieldValues = cloneObject(values);
3200
3289
  }
3201
3290
  else {
3291
+ const resetCallId = _resetCallId;
3202
3292
  await executeBuiltInValidation({
3203
3293
  fields: _fields,
3204
3294
  eventType: EVENTS.SUBMIT,
3205
3295
  });
3296
+ if (resetCallId !== _resetCallId) {
3297
+ return;
3298
+ }
3206
3299
  unset(_formState.errors, ROOT_ERROR_TYPE);
3207
3300
  }
3208
3301
  if (_names.disabled.size) {
@@ -3260,7 +3353,7 @@ function createFormControl(props = {}) {
3260
3353
  : _getDirty();
3261
3354
  }
3262
3355
  if (!options.keepError) {
3263
- cancelDelayedError(name);
3356
+ cancelDelayedErrorTree(name);
3264
3357
  unset(_formState.errors, name);
3265
3358
  _setValid();
3266
3359
  }
@@ -3572,6 +3665,7 @@ function useForm(props = {}) {
3572
3665
  const _formControl = React.useRef(undefined);
3573
3666
  const _values = React.useRef(undefined);
3574
3667
  const _formControlProp = React.useRef(props.formControl);
3668
+ const _hadValidate = React.useRef(!!props.validate);
3575
3669
  const [formState, updateFormState] = React.useState(() => ({
3576
3670
  ...cloneObject(DEFAULT_FORM_STATE),
3577
3671
  isLoading: isFunction(props.defaultValues),
@@ -3602,7 +3696,7 @@ function useForm(props = {}) {
3602
3696
  }
3603
3697
  }
3604
3698
  const control = _formControl.current.control;
3605
- control._options = props;
3699
+ control._options = { ...props, validate: props.validate };
3606
3700
  const getCurrentFormState = () => ({
3607
3701
  ...control._formState,
3608
3702
  defaultValues: control._defaultValues,
@@ -3643,6 +3737,13 @@ function useForm(props = {}) {
3643
3737
  control._focusError();
3644
3738
  }
3645
3739
  }, [control, props.errors]);
3740
+ React.useEffect(() => {
3741
+ var _a;
3742
+ if (_hadValidate.current && !props.validate) {
3743
+ (_a = _formControl.current) === null || _a === void 0 ? void 0 : _a.clearErrors(FORM_ERROR_TYPE);
3744
+ }
3745
+ _hadValidate.current = !!props.validate;
3746
+ }, [props.validate]);
3646
3747
  React.useEffect(() => {
3647
3748
  props.shouldUnregister &&
3648
3749
  control._subjects.state.next({