react-hook-form 7.11.1 → 7.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -148,11 +148,18 @@ function useController({ name, rules, defaultValue, control, shouldUnregister, }
148
148
  control: control || methods.control,
149
149
  name,
150
150
  });
151
+ function updateIsMounted(name, value) {
152
+ const field = get(fieldsRef.current, name);
153
+ if (field && field._f) {
154
+ field._f.mount = value;
155
+ }
156
+ }
151
157
  React.useEffect(() => {
152
158
  const controllerSubscription = subjectsRef.current.control.subscribe({
153
159
  next: (data) => (!data.name || name === data.name) &&
154
160
  setInputStateValue(get(data.values, name)),
155
161
  });
162
+ updateIsMounted(name, true);
156
163
  return () => {
157
164
  controllerSubscription.unsubscribe();
158
165
  const shouldUnmountField = shouldUnmount || shouldUnregister;
@@ -162,10 +169,7 @@ function useController({ name, rules, defaultValue, control, shouldUnregister, }
162
169
  unregister(name);
163
170
  }
164
171
  else {
165
- const field = get(fieldsRef.current, name);
166
- if (field && field._f) {
167
- field._f.mount = false;
168
- }
172
+ updateIsMounted(name, false);
169
173
  }
170
174
  };
171
175
  }, [name]);
@@ -370,7 +374,7 @@ function setDirtyFields(values, defaultValues, dirtyFields, parentNode, parentNa
370
374
  var setFieldArrayDirtyFields = (values, defaultValues, dirtyFields) => deepMerge(setDirtyFields(values, defaultValues, dirtyFields.slice(0, values.length)), setDirtyFields(defaultValues, values, dirtyFields.slice(0, values.length)));
371
375
 
372
376
  function append(data, value) {
373
- return [...data, ...convertToArrayPayload(value)];
377
+ return [...convertToArrayPayload(data), ...convertToArrayPayload(value)];
374
378
  }
375
379
 
376
380
  var fillEmptyArray = (value) => Array.isArray(value) ? Array(value.length).fill(undefined) : undefined;
@@ -395,7 +399,7 @@ var moveArrayAt = (data, from, to) => {
395
399
  };
396
400
 
397
401
  function prepend(data, value) {
398
- return [...convertToArrayPayload(value), ...data];
402
+ return [...convertToArrayPayload(value), ...convertToArrayPayload(data)];
399
403
  }
400
404
 
401
405
  function removeAtIndexes(data, indexes) {
@@ -503,6 +507,8 @@ const useFieldArray = ({ control, name, keyName = 'id', shouldUnregister, }) =>
503
507
  cleanup(formStateRef.current.dirtyFields);
504
508
  }
505
509
  subjectsRef.current.state.next({
510
+ dirtyFields: formStateRef.current
511
+ .dirtyFields,
506
512
  isDirty: getIsDirty(name, omitKey(updatedFieldArrayValues)),
507
513
  errors: formStateRef.current.errors,
508
514
  isValid: formStateRef.current.isValid,
@@ -938,6 +944,14 @@ var validateField = async ({ _f: { ref, refs, required, maxLength, minLength, mi
938
944
  return error;
939
945
  };
940
946
 
947
+ var debounce = (callback, wait) => {
948
+ let timer = 0;
949
+ return (...args) => {
950
+ clearTimeout(timer);
951
+ timer = setTimeout(() => callback(...args), wait);
952
+ };
953
+ };
954
+
941
955
  var getValidationModes = (mode) => ({
942
956
  isOnSubmit: !mode || mode === VALIDATION_MODE.onSubmit,
943
957
  isOnBlur: mode === VALIDATION_MODE.onBlur,
@@ -997,7 +1011,7 @@ class Subject {
997
1011
  }
998
1012
 
999
1013
  const isWindowUndefined = typeof window === 'undefined';
1000
- function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_MODE.onChange, resolver, context, defaultValues = {}, shouldFocusError = true, shouldUseNativeValidation, shouldUnregister, criteriaMode, } = {}) {
1014
+ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_MODE.onChange, resolver, context, defaultValues = {}, shouldFocusError = true, delayError, shouldUseNativeValidation, shouldUnregister, criteriaMode, } = {}) {
1001
1015
  const [formState, updateFormState] = React.useState({
1002
1016
  isDirty: false,
1003
1017
  isValidating: false,
@@ -1026,6 +1040,7 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
1026
1040
  const contextRef = React.useRef(context);
1027
1041
  const inFieldArrayActionRef = React.useRef(false);
1028
1042
  const isMountedRef = React.useRef(false);
1043
+ const _delayCallback = React.useRef();
1029
1044
  const subjectsRef = React.useRef({
1030
1045
  watch: new Subject(),
1031
1046
  control: new Subject(),
@@ -1046,22 +1061,35 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
1046
1061
  const isFieldWatched = (name) => namesRef.current.watchAll ||
1047
1062
  namesRef.current.watch.has(name) ||
1048
1063
  namesRef.current.watch.has((name.match(/\w+/) || [])[0]);
1049
- const shouldRenderBaseOnError = React.useCallback(async (shouldSkipRender, name, error, inputState, isValidFromResolver, isWatched) => {
1064
+ const updateErrorState = (name, error) => {
1065
+ set(formStateRef.current.errors, name, error);
1066
+ subjectsRef.current.state.next({
1067
+ errors: formStateRef.current.errors,
1068
+ });
1069
+ };
1070
+ const shouldRenderBaseOnError = React.useCallback(async (shouldSkipRender, name, error, fieldState, isValidFromResolver, isWatched) => {
1050
1071
  const previousError = get(formStateRef.current.errors, name);
1051
1072
  const isValid = readFormStateRef.current.isValid
1052
1073
  ? resolver
1053
1074
  ? isValidFromResolver
1054
1075
  : await validateForm(fieldsRef.current, true)
1055
1076
  : false;
1056
- error
1057
- ? set(formStateRef.current.errors, name, error)
1058
- : unset(formStateRef.current.errors, name);
1077
+ if (delayError && error) {
1078
+ _delayCallback.current =
1079
+ _delayCallback.current || debounce(updateErrorState, delayError);
1080
+ _delayCallback.current(name, error);
1081
+ }
1082
+ else {
1083
+ error
1084
+ ? set(formStateRef.current.errors, name, error)
1085
+ : unset(formStateRef.current.errors, name);
1086
+ }
1059
1087
  if ((isWatched ||
1060
1088
  (error ? !deepEqual(previousError, error, true) : previousError) ||
1061
- !isEmptyObject(inputState) ||
1089
+ !isEmptyObject(fieldState) ||
1062
1090
  formStateRef.current.isValid !== isValid) &&
1063
1091
  !shouldSkipRender) {
1064
- const updatedFormState = Object.assign(Object.assign({}, inputState), { isValid: !!isValid, errors: formStateRef.current.errors, name });
1092
+ const updatedFormState = Object.assign(Object.assign({}, fieldState), { isValid: !!isValid, errors: formStateRef.current.errors, name });
1065
1093
  formStateRef.current = Object.assign(Object.assign({}, formStateRef.current), updatedFormState);
1066
1094
  subjectsRef.current.state.next(isWatched ? { name } : updatedFormState);
1067
1095
  }
@@ -1237,7 +1265,7 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
1237
1265
  readFormStateRef.current.isValid && updateIsValid();
1238
1266
  return isValid;
1239
1267
  }, [executeResolverValidation, executeInlineValidation]);
1240
- const updateIsValidAndInputValue = (name, ref) => {
1268
+ const updateIsValidAndInputValue = (name, ref, shouldSkipValueAs) => {
1241
1269
  const field = get(fieldsRef.current, name);
1242
1270
  if (field) {
1243
1271
  const isValueUndefined = isUndefined(field._f.value);
@@ -1250,6 +1278,9 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
1250
1278
  if (ref && ref.defaultChecked) {
1251
1279
  field._f.value = getFieldValue(field);
1252
1280
  }
1281
+ else if (shouldSkipValueAs) {
1282
+ field._f.value = defaultValue;
1283
+ }
1253
1284
  else {
1254
1285
  setFieldValue(name, defaultValue);
1255
1286
  }
@@ -1264,10 +1295,12 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
1264
1295
  const isValid = resolver
1265
1296
  ? isEmptyObject((await resolverRef.current(Object.assign(Object.assign({}, getFieldsValues(fieldsRef)), values), contextRef.current, getResolverOptions(namesRef.current.mount, fieldsRef.current, criteriaMode, shouldUseNativeValidation))).errors)
1266
1297
  : await validateForm(fieldsRef.current, true);
1267
- isValid !== formStateRef.current.isValid &&
1298
+ if (isValid !== formStateRef.current.isValid) {
1299
+ formStateRef.current.isValid = isValid;
1268
1300
  subjectsRef.current.state.next({
1269
1301
  isValid,
1270
1302
  });
1303
+ }
1271
1304
  }, [criteriaMode, shouldUseNativeValidation]);
1272
1305
  const setValues = React.useCallback((name, value, options) => Object.entries(value).forEach(([fieldKey, fieldValue]) => {
1273
1306
  const fieldName = `${name}.${fieldKey}`;
@@ -1307,9 +1340,36 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
1307
1340
  isFieldWatched(name) && subjectsRef.current.state.next({});
1308
1341
  subjectsRef.current.watch.next({ name, values: getValues() });
1309
1342
  };
1310
- const handleChange = React.useCallback(async ({ type, target, target: { value, name, type: inputType } }) => {
1343
+ const handleValidate = async (target, fieldState, isWatched, isBlurEvent) => {
1311
1344
  let error;
1312
1345
  let isValid;
1346
+ let name = target.name;
1347
+ const field = get(fieldsRef.current, name);
1348
+ if (resolver) {
1349
+ const { errors } = await resolverRef.current(getFieldsValues(fieldsRef), contextRef.current, getResolverOptions([name], fieldsRef.current, criteriaMode, shouldUseNativeValidation));
1350
+ error = get(errors, name);
1351
+ if (isCheckBoxInput(target) && !error) {
1352
+ const parentNodeName = getNodeParentName(name);
1353
+ const currentError = get(errors, parentNodeName, {});
1354
+ currentError.type && currentError.message && (error = currentError);
1355
+ if (currentError || get(formStateRef.current.errors, parentNodeName)) {
1356
+ name = parentNodeName;
1357
+ }
1358
+ }
1359
+ isValid = isEmptyObject(errors);
1360
+ }
1361
+ else {
1362
+ error = (await validateField(field, isValidateAllFieldCriteria, shouldUseNativeValidation))[name];
1363
+ }
1364
+ !isBlurEvent &&
1365
+ subjectsRef.current.watch.next({
1366
+ name,
1367
+ type: target.type,
1368
+ values: getValues(),
1369
+ });
1370
+ shouldRenderBaseOnError(false, name, error, fieldState, isValid, isWatched);
1371
+ };
1372
+ const handleChange = React.useCallback(async ({ type, target, target: { value, name, type: inputType } }) => {
1313
1373
  const field = get(fieldsRef.current, name);
1314
1374
  if (field) {
1315
1375
  let inputValue = inputType ? getFieldValue(field) : undefined;
@@ -1325,8 +1385,8 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
1325
1385
  if (!isUndefined(inputValue)) {
1326
1386
  field._f.value = inputValue;
1327
1387
  }
1328
- const inputState = updateTouchAndDirtyState(name, field._f.value, isBlurEvent, false);
1329
- const shouldRender = !isEmptyObject(inputState) || isWatched;
1388
+ const fieldState = updateTouchAndDirtyState(name, field._f.value, isBlurEvent, false);
1389
+ const shouldRender = !isEmptyObject(fieldState) || isWatched;
1330
1390
  if (shouldSkipValidation) {
1331
1391
  !isBlurEvent &&
1332
1392
  subjectsRef.current.watch.next({
@@ -1335,35 +1395,12 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
1335
1395
  values: getValues(),
1336
1396
  });
1337
1397
  return (shouldRender &&
1338
- subjectsRef.current.state.next(isWatched ? { name } : Object.assign(Object.assign({}, inputState), { name })));
1398
+ subjectsRef.current.state.next(isWatched ? { name } : Object.assign(Object.assign({}, fieldState), { name })));
1339
1399
  }
1340
1400
  subjectsRef.current.state.next({
1341
1401
  isValidating: true,
1342
1402
  });
1343
- if (resolver) {
1344
- const { errors } = await resolverRef.current(getFieldsValues(fieldsRef), contextRef.current, getResolverOptions([name], fieldsRef.current, criteriaMode, shouldUseNativeValidation));
1345
- error = get(errors, name);
1346
- if (isCheckBoxInput(target) && !error) {
1347
- const parentNodeName = getNodeParentName(name);
1348
- const currentError = get(errors, parentNodeName, {});
1349
- currentError.type && currentError.message && (error = currentError);
1350
- if (currentError ||
1351
- get(formStateRef.current.errors, parentNodeName)) {
1352
- name = parentNodeName;
1353
- }
1354
- }
1355
- isValid = isEmptyObject(errors);
1356
- }
1357
- else {
1358
- error = (await validateField(field, isValidateAllFieldCriteria, shouldUseNativeValidation))[name];
1359
- }
1360
- !isBlurEvent &&
1361
- subjectsRef.current.watch.next({
1362
- name,
1363
- type,
1364
- values: getValues(),
1365
- });
1366
- shouldRenderBaseOnError(false, name, error, inputState, isValid, isWatched);
1403
+ handleValidate(target, fieldState, isWatched, isBlurEvent);
1367
1404
  }
1368
1405
  }, []);
1369
1406
  const getValues = (fieldNames) => {
@@ -1465,7 +1502,7 @@ function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_
1465
1502
  _f: Object.assign(Object.assign(Object.assign({}, (field && field._f ? field._f : { ref: { name } })), { name, mount: true }), options),
1466
1503
  });
1467
1504
  namesRef.current.mount.add(name);
1468
- !field && updateIsValidAndInputValue(name);
1505
+ !field && updateIsValidAndInputValue(name, undefined, true);
1469
1506
  return isWindowUndefined
1470
1507
  ? { name: name }
1471
1508
  : {