react-hook-form 7.80.0 → 7.82.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.
@@ -1 +1 @@
1
- {"version":3,"file":"createFormControl.d.ts","sourceRoot":"","sources":["../../src/logic/createFormControl.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAcV,WAAW,EAiBX,YAAY,EAKZ,aAAa,EAYd,MAAM,UAAU,CAAC;AAyDlB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;CAY9B,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,YAAY,SAAS,WAAW,GAAG,WAAW,EAC9C,QAAQ,GAAG,GAAG,EACd,kBAAkB,GAAG,YAAY,EAEjC,KAAK,GAAE,YAAY,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAM,GACnE,IAAI,CACL,aAAa,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAC,EACzD,WAAW,CACZ,GAAG;IACF,WAAW,EAAE,IAAI,CACf,aAAa,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAC,EACzD,WAAW,CACZ,CAAC;CACH,CA0yDA"}
1
+ {"version":3,"file":"createFormControl.d.ts","sourceRoot":"","sources":["../../src/logic/createFormControl.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAcV,WAAW,EAkBX,YAAY,EAKZ,aAAa,EAYd,MAAM,UAAU,CAAC;AA0DlB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;CAY9B,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,YAAY,SAAS,WAAW,GAAG,WAAW,EAC9C,QAAQ,GAAG,GAAG,EACd,kBAAkB,GAAG,YAAY,EAEjC,KAAK,GAAE,YAAY,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAM,GACnE,IAAI,CACL,aAAa,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAC,EACzD,WAAW,CACZ,GAAG;IACF,WAAW,EAAE,IAAI,CACf,aAAa,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAC,EACzD,WAAW,CACZ,CAAC;CACH,CA62DA"}
@@ -1,4 +1,5 @@
1
- declare function markFieldsDirty<T>(data: T, fields?: Record<string, any>): Record<string, any>;
2
- export default function getDirtyFields<T>(data: T, formValues: T, dirtyFieldsFromValues?: Record<Extract<keyof T, string>, ReturnType<typeof markFieldsDirty> | boolean>): Record<Extract<keyof T, string>, boolean | Record<string, any>>;
1
+ import type { FieldRefs } from '../types';
2
+ declare function markFieldsDirty<T>(data: T, fields?: Record<string, any>, fieldRefs?: Record<string, any>): Record<string, any>;
3
+ export default function getDirtyFields<T>(data: T, formValues: T, dirtyFieldsFromValues?: Record<Extract<keyof T, string>, ReturnType<typeof markFieldsDirty> | boolean>, fieldRefs?: FieldRefs): Record<Extract<keyof T, string>, boolean | Record<string, any>>;
3
4
  export {};
4
5
  //# sourceMappingURL=getDirtyFields.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getDirtyFields.d.ts","sourceRoot":"","sources":["../../src/logic/getDirtyFields.ts"],"names":[],"mappings":"AAWA,iBAAS,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,uBAapE;AAmCD,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,CAAC,EACtC,IAAI,EAAE,CAAC,EACP,UAAU,EAAE,CAAC,EACb,qBAAqB,CAAC,EAAE,MAAM,CAC5B,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EACxB,UAAU,CAAC,OAAO,eAAe,CAAC,GAAG,OAAO,CAC7C,mEA6BF"}
1
+ {"version":3,"file":"getDirtyFields.d.ts","sourceRoot":"","sources":["../../src/logic/getDirtyFields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AA8B1C,iBAAS,eAAe,CAAC,CAAC,EACxB,IAAI,EAAE,CAAC,EACP,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EAChC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,uBAsBhC;AAED,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,CAAC,EACtC,IAAI,EAAE,CAAC,EACP,UAAU,EAAE,CAAC,EACb,qBAAqB,CAAC,EAAE,MAAM,CAC5B,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EACxB,UAAU,CAAC,OAAO,eAAe,CAAC,GAAG,OAAO,CAC7C,EACD,SAAS,CAAC,EAAE,SAAS,mEAwCtB"}
@@ -182,6 +182,24 @@ function extractFormValues(fieldsState, formValues) {
182
182
  return values;
183
183
  }
184
184
 
185
+ const flatten = (obj) => {
186
+ const output = {};
187
+ for (const key of Object.keys(obj)) {
188
+ if (isObjectType(obj[key]) &&
189
+ obj[key] !== null &&
190
+ !isDateObject(obj[key])) {
191
+ const nested = flatten(obj[key]);
192
+ for (const nestedKey of Object.keys(nested)) {
193
+ output[`${key}.${nestedKey}`] = nested[nestedKey];
194
+ }
195
+ }
196
+ else {
197
+ output[key] = obj[key];
198
+ }
199
+ }
200
+ return output;
201
+ };
202
+
185
203
  const IS_KEY_RE = /^\w*$/;
186
204
  var isKey = (value) => IS_KEY_RE.test(value);
187
205
 
@@ -293,6 +311,9 @@ function unset(object, path) {
293
311
  : isKey(path)
294
312
  ? [path]
295
313
  : stringToPath(path);
314
+ if (paths.some((segment) => PROTOTYPE_KEYWORDS.includes(String(segment)))) {
315
+ return object;
316
+ }
296
317
  const childObject = paths.length === 1 ? object : baseGet(object, paths);
297
318
  const index = paths.length - 1;
298
319
  const key = paths[index];
@@ -332,12 +353,33 @@ var objectHasFunction = (data) => {
332
353
  function isTraversable(value) {
333
354
  return Array.isArray(value) || (isObject(value) && !objectHasFunction(value));
334
355
  }
335
- function markFieldsDirty(data, fields = {}) {
356
+ function isRegisteredLeaf(fieldRef) {
357
+ return !!(fieldRef && '_f' in fieldRef);
358
+ }
359
+ function isEmptyDirtyContainer(value) {
360
+ return Array.isArray(value)
361
+ ? !value.some((item) => !isUndefined(item))
362
+ : !Object.keys(value).length;
363
+ }
364
+ function clearDirtyField(container, key) {
365
+ if (Array.isArray(container)) {
366
+ container[key] = undefined;
367
+ }
368
+ else {
369
+ delete container[key];
370
+ }
371
+ }
372
+ function markFieldsDirty(data, fields = {}, fieldRefs) {
336
373
  for (const key in data) {
337
374
  const value = data[key];
338
- if (isTraversable(value)) {
375
+ const fieldRef = fieldRefs && fieldRefs[key];
376
+ if (isTraversable(value) &&
377
+ (!Array.isArray(value) || !isRegisteredLeaf(fieldRef))) {
339
378
  fields[key] = Array.isArray(value) ? [] : {};
340
- markFieldsDirty(value, fields[key]);
379
+ markFieldsDirty(value, fields[key], fieldRef);
380
+ if (isEmptyDirtyContainer(fields[key])) {
381
+ clearDirtyField(fields, key);
382
+ }
341
383
  }
342
384
  else if (!isUndefined(value)) {
343
385
  fields[key] = true;
@@ -345,49 +387,33 @@ function markFieldsDirty(data, fields = {}) {
345
387
  }
346
388
  return fields;
347
389
  }
348
- function pruneDirtyFields(value) {
349
- if (value === false) {
350
- return undefined;
351
- }
352
- if (value === true) {
353
- return true;
354
- }
355
- if (Array.isArray(value)) {
356
- const result = value.map((value) => pruneDirtyFields(value));
357
- return (result.some((value) => value !== undefined) ? result : undefined);
358
- }
359
- if (isObject(value)) {
360
- const result = {};
361
- for (const key in value) {
362
- const pruned = pruneDirtyFields(value[key]);
363
- if (!isUndefined(pruned)) {
364
- result[key] = pruned;
365
- }
366
- }
367
- return (Object.keys(result).length ? result : undefined);
368
- }
369
- return undefined;
370
- }
371
- function getDirtyFields(data, formValues, dirtyFieldsFromValues) {
390
+ function getDirtyFields(data, formValues, dirtyFieldsFromValues, fieldRefs) {
372
391
  if (!dirtyFieldsFromValues) {
373
- dirtyFieldsFromValues = markFieldsDirty(formValues);
392
+ dirtyFieldsFromValues = markFieldsDirty(formValues, {}, fieldRefs);
374
393
  }
375
394
  for (const key in data) {
376
395
  const value = data[key];
377
- if (isTraversable(value)) {
396
+ const fieldRef = fieldRefs && fieldRefs[key];
397
+ if (isTraversable(value) &&
398
+ (!Array.isArray(value) || !isRegisteredLeaf(fieldRef))) {
378
399
  if (isUndefined(formValues) || isPrimitive(dirtyFieldsFromValues[key])) {
379
- dirtyFieldsFromValues[key] = markFieldsDirty(value, Array.isArray(value) ? [] : {});
400
+ dirtyFieldsFromValues[key] = markFieldsDirty(value, Array.isArray(value) ? [] : {}, fieldRef);
380
401
  }
381
402
  else {
382
- getDirtyFields(value, isNullOrUndefined(formValues) ? {} : formValues[key], dirtyFieldsFromValues[key]);
403
+ getDirtyFields(value, isNullOrUndefined(formValues) ? {} : formValues[key], dirtyFieldsFromValues[key], fieldRef);
404
+ }
405
+ if (isEmptyDirtyContainer(dirtyFieldsFromValues[key])) {
406
+ clearDirtyField(dirtyFieldsFromValues, key);
383
407
  }
384
408
  }
409
+ else if (deepEqual(value, formValues[key])) {
410
+ clearDirtyField(dirtyFieldsFromValues, key);
411
+ }
385
412
  else {
386
- const formValue = formValues[key];
387
- dirtyFieldsFromValues[key] = !deepEqual(value, formValue);
413
+ dirtyFieldsFromValues[key] = true;
388
414
  }
389
415
  }
390
- return pruneDirtyFields(dirtyFieldsFromValues) || {};
416
+ return dirtyFieldsFromValues;
391
417
  }
392
418
 
393
419
  var getEventValue = (event) => isObject(event) && event.target
@@ -620,7 +646,7 @@ var shouldSubscribeByName = (name, signalName, exact) => !name ||
620
646
  name === signalName ||
621
647
  convertToArrayPayload(name).some((currentName) => currentName &&
622
648
  (exact
623
- ? currentName === signalName
649
+ ? currentName === signalName || currentName.startsWith(signalName + '.')
624
650
  : currentName.startsWith(signalName) ||
625
651
  signalName.startsWith(currentName)));
626
652
 
@@ -905,8 +931,8 @@ function createFormControl(props = {}) {
905
931
  watch: new Set(),
906
932
  registerName: new Set(),
907
933
  };
908
- let delayErrorCallback;
909
- let timer = 0;
934
+ const delayErrorCallbacks = {};
935
+ const timers = {};
910
936
  let _valuesSubscriberCount = 0;
911
937
  let _validationModeBeforeSubmit = getValidationModes(_options.mode);
912
938
  let _validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
@@ -929,10 +955,11 @@ function createFormControl(props = {}) {
929
955
  array: createSubject(),
930
956
  state: createSubject(),
931
957
  };
958
+ let _setValidCallId = 0;
932
959
  const shouldDisplayAllAssociatedErrors = _options.criteriaMode === VALIDATION_MODE.all;
933
- const debounce = (callback) => (wait) => {
934
- clearTimeout(timer);
935
- timer = setTimeout(callback, wait);
960
+ const debounce = (name, callback) => (wait) => {
961
+ clearTimeout(timers[name]);
962
+ timers[name] = setTimeout(callback, wait);
936
963
  };
937
964
  const _setValid = async (shouldUpdateValid) => {
938
965
  if (_state.keepIsValid) {
@@ -942,10 +969,11 @@ function createFormControl(props = {}) {
942
969
  (_proxyFormState.isValid ||
943
970
  _proxySubscribeFormState.isValid ||
944
971
  shouldUpdateValid)) {
972
+ const callId = ++_setValidCallId;
945
973
  let isValid;
946
974
  if (_options.resolver) {
947
975
  isValid = isEmptyObject((await _runSchema()).errors);
948
- _updateIsValidating();
976
+ callId === _setValidCallId && _updateIsValidating();
949
977
  }
950
978
  else {
951
979
  isValid = await executeBuiltInValidation({
@@ -954,7 +982,7 @@ function createFormControl(props = {}) {
954
982
  eventType: EVENTS.VALID,
955
983
  });
956
984
  }
957
- if (isValid !== _formState.isValid) {
985
+ if (callId === _setValidCallId && isValid !== _formState.isValid) {
958
986
  _subjects.state.next({
959
987
  isValid,
960
988
  });
@@ -981,7 +1009,7 @@ function createFormControl(props = {}) {
981
1009
  }
982
1010
  };
983
1011
  const _updateDirtyFields = () => {
984
- _formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
1012
+ _formState.dirtyFields = getDirtyFields(_defaultValues, _formValues, undefined, _fields);
985
1013
  };
986
1014
  const _setFieldArray = (name, values = [], method, args, shouldSetValues = true, shouldUpdateFieldsAndState = true) => {
987
1015
  if (args && method && !_options.disabled) {
@@ -1087,7 +1115,9 @@ function createFormControl(props = {}) {
1087
1115
  const output = {
1088
1116
  name,
1089
1117
  };
1090
- if (!_options.disabled) {
1118
+ // an explicit programmatic update (e.g. setValue with shouldDirty: true)
1119
+ // opts into dirty tracking even when the form is disabled
1120
+ if (!_options.disabled || shouldDirty === true) {
1091
1121
  if (!isBlurEvent || shouldDirty) {
1092
1122
  const isCurrentFieldPristine = deepEqual(get(_defaultValues, name), fieldValue);
1093
1123
  if (_proxyFormState.isDirty || _proxySubscribeFormState.isDirty) {
@@ -1098,7 +1128,7 @@ function createFormControl(props = {}) {
1098
1128
  }
1099
1129
  isPreviousDirty = !!get(_formState.dirtyFields, name);
1100
1130
  if (isCurrentFieldPristine !== _formState.isDirty) {
1101
- _formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
1131
+ _formState.dirtyFields = getDirtyFields(_defaultValues, _formValues, undefined, _fields);
1102
1132
  }
1103
1133
  else {
1104
1134
  isCurrentFieldPristine
@@ -1134,12 +1164,12 @@ function createFormControl(props = {}) {
1134
1164
  isBoolean(isValid) &&
1135
1165
  _formState.isValid !== isValid;
1136
1166
  if (_options.delayError && error) {
1137
- delayErrorCallback = debounce(() => updateErrors(name, error));
1138
- delayErrorCallback(_options.delayError);
1167
+ delayErrorCallbacks[name] = debounce(name, () => updateErrors(name, error));
1168
+ delayErrorCallbacks[name](_options.delayError);
1139
1169
  }
1140
1170
  else {
1141
- clearTimeout(timer);
1142
- delayErrorCallback = null;
1171
+ clearTimeout(timers[name]);
1172
+ delete delayErrorCallbacks[name];
1143
1173
  error
1144
1174
  ? set(_formState.errors, name, error)
1145
1175
  : unset(_formState.errors, name);
@@ -1292,9 +1322,8 @@ function createFormControl(props = {}) {
1292
1322
  }
1293
1323
  _names.unMount = new Set();
1294
1324
  };
1295
- const _getDirty = (name, data) => !_options.disabled &&
1296
- (name && data && set(_formValues, name, data),
1297
- !deepEqual(_state.mount ? _formValues : _defaultValues, _defaultValues));
1325
+ const _getDirty = (name, data) => (name && data && set(_formValues, name, data),
1326
+ !deepEqual(_state.mount ? _formValues : _defaultValues, _defaultValues));
1298
1327
  const _getWatch = (names, defaultValue, isGlobal) => generateWatchOutput(names, _names, {
1299
1328
  ...(_state.mount
1300
1329
  ? _formValues
@@ -1354,7 +1383,10 @@ function createFormControl(props = {}) {
1354
1383
  }
1355
1384
  (options.shouldDirty || options.shouldTouch) &&
1356
1385
  updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty, !skipRender);
1357
- options.shouldValidate && trigger(name);
1386
+ options.shouldValidate &&
1387
+ trigger(name, {
1388
+ delayError: options.delayError,
1389
+ });
1358
1390
  };
1359
1391
  const setFieldValues = (name, value, options, skipClone = false, skipRender = false) => {
1360
1392
  for (const fieldKey in value) {
@@ -1431,8 +1463,11 @@ function createFormControl(props = {}) {
1431
1463
  ..._formValues,
1432
1464
  ...updatedFormValues,
1433
1465
  };
1466
+ const flattenedUpdates = flatten(updatedFormValues);
1434
1467
  for (const fieldName of _names.mount) {
1435
- _setValue(fieldName, get(updatedFormValues, fieldName), options, true, true);
1468
+ if (fieldName in flattenedUpdates) {
1469
+ _setValue(fieldName, flattenedUpdates[fieldName], options, true, true);
1470
+ }
1436
1471
  }
1437
1472
  _subjects.state.next({
1438
1473
  ..._formState,
@@ -1476,7 +1511,8 @@ function createFormControl(props = {}) {
1476
1511
  if (isBlurEvent) {
1477
1512
  if (!target || !target.readOnly) {
1478
1513
  field._f.onBlur && field._f.onBlur(event);
1479
- delayErrorCallback && delayErrorCallback(0);
1514
+ const pendingDelayError = delayErrorCallbacks[name];
1515
+ pendingDelayError && pendingDelayError(0);
1480
1516
  }
1481
1517
  }
1482
1518
  else if (field._f.onChange) {
@@ -1591,6 +1627,18 @@ function createFormControl(props = {}) {
1591
1627
  eventType: EVENTS.TRIGGER,
1592
1628
  });
1593
1629
  }
1630
+ if (options.delayError && _options.delayError && isString(name)) {
1631
+ const error = get(_formState.errors, name);
1632
+ if (error) {
1633
+ unset(_formState.errors, name);
1634
+ delayErrorCallbacks[name] = debounce(name, () => updateErrors(name, error));
1635
+ delayErrorCallbacks[name](_options.delayError);
1636
+ }
1637
+ else {
1638
+ clearTimeout(timers[name]);
1639
+ delete delayErrorCallbacks[name];
1640
+ }
1641
+ }
1594
1642
  _subjects.state.next({
1595
1643
  ...(!isString(name) ||
1596
1644
  ((_proxyFormState.isValid || _proxySubscribeFormState.isValid) &&
@@ -1959,6 +2007,7 @@ function createFormControl(props = {}) {
1959
2007
  const cloneUpdatedValues = cloneObject(updatedValues);
1960
2008
  const isEmptyResetValues = isEmptyObject(formValues);
1961
2009
  const values = cloneUpdatedValues;
2010
+ const fieldRefs = _fields;
1962
2011
  if (!keepStateOptions.keepDefaultValues) {
1963
2012
  _defaultValues = updatedValues;
1964
2013
  }
@@ -1966,7 +2015,7 @@ function createFormControl(props = {}) {
1966
2015
  if (keepStateOptions.keepDirtyValues) {
1967
2016
  const fieldsToCheck = new Set([
1968
2017
  ..._names.mount,
1969
- ...Object.keys(getDirtyFields(_defaultValues, _formValues)),
2018
+ ...Object.keys(getDirtyFields(_defaultValues, _formValues, undefined, fieldRefs)),
1970
2019
  ]);
1971
2020
  for (const fieldName of Array.from(fieldsToCheck)) {
1972
2021
  const isDirty = get(_formState.dirtyFields, fieldName);
@@ -2024,6 +2073,8 @@ function createFormControl(props = {}) {
2024
2073
  values: { ...values },
2025
2074
  });
2026
2075
  _subjects.state.next({
2076
+ name: undefined,
2077
+ type: undefined,
2027
2078
  values: { ...values },
2028
2079
  });
2029
2080
  }
@@ -2067,10 +2118,10 @@ function createFormControl(props = {}) {
2067
2118
  ? {}
2068
2119
  : keepStateOptions.keepDirtyValues
2069
2120
  ? keepStateOptions.keepDefaultValues && _formValues
2070
- ? getDirtyFields(_defaultValues, _formValues)
2121
+ ? getDirtyFields(_defaultValues, _formValues, undefined, fieldRefs)
2071
2122
  : _formState.dirtyFields
2072
2123
  : keepStateOptions.keepDefaultValues && formValues
2073
- ? getDirtyFields(_defaultValues, formValues)
2124
+ ? getDirtyFields(_defaultValues, formValues, undefined, fieldRefs)
2074
2125
  : keepStateOptions.keepDirty
2075
2126
  ? _formState.dirtyFields
2076
2127
  : {},
@@ -2106,9 +2157,15 @@ function createFormControl(props = {}) {
2106
2157
  }
2107
2158
  };
2108
2159
  const _setFormState = (updatedFormState) => {
2160
+ // `name`, `type`, and `values` describe the event that produced this
2161
+ // update, not the form's persisted state (they aren't part of
2162
+ // `FormState`). Merging them in would leak a stale `name`/`type` from
2163
+ // one event into a later, unrelated notification that doesn't specify
2164
+ // its own.
2165
+ const { name, type, values, ...formState } = updatedFormState;
2109
2166
  _formState = {
2110
2167
  ..._formState,
2111
- ...updatedFormState,
2168
+ ...formState,
2112
2169
  };
2113
2170
  };
2114
2171
  const _resetDefaultValues = () => isFunction(_options.defaultValues) &&
@@ -2121,7 +2178,7 @@ function createFormControl(props = {}) {
2121
2178
  const resetDefaultValues = (values, options = {}) => {
2122
2179
  _defaultValues = cloneObject(values);
2123
2180
  if (!options.keepDirty) {
2124
- const newDirtyFields = getDirtyFields(_defaultValues, _formValues);
2181
+ const newDirtyFields = getDirtyFields(_defaultValues, _formValues, undefined, _fields);
2125
2182
  _formState.dirtyFields = newDirtyFields;
2126
2183
  _formState.isDirty = !isEmptyObject(newDirtyFields);
2127
2184
  }