react-hook-form 7.86.0 → 7.87.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.
@@ -22,6 +22,24 @@ var getEventValue = (event) => isObject(event) && event.target
22
22
  : event.target.value
23
23
  : event;
24
24
 
25
+ const FIELD_PATH_RE = /[.[\]'"]/;
26
+ var stringToPath = (input) => input.split(FIELD_PATH_RE).filter(Boolean);
27
+
28
+ function getNullAncestorValue(control, name) {
29
+ const segments = stringToPath(name);
30
+ let formValues = control._formValues;
31
+ let defaultValues = control._defaultValues;
32
+ for (let i = 0; i < segments.length - 1; i++) {
33
+ const key = segments[i];
34
+ formValues = formValues == null ? formValues : formValues[key];
35
+ defaultValues = defaultValues == null ? defaultValues : defaultValues[key];
36
+ if (formValues === null || defaultValues === null) {
37
+ return null;
38
+ }
39
+ }
40
+ return undefined;
41
+ }
42
+
25
43
  var isNameInFieldArray = (names, name) => name
26
44
  .split('.')
27
45
  .some((part, index, arr) => !isNaN(Number(part)) && names.has(arr.slice(0, index).join('.')));
@@ -86,9 +104,6 @@ var isKey = (value) => IS_KEY_RE.test(value);
86
104
 
87
105
  var isUndefined = (val) => val === undefined;
88
106
 
89
- const FIELD_PATH_RE = /[.[\]'"]/;
90
- var stringToPath = (input) => input.split(FIELD_PATH_RE).filter(Boolean);
91
-
92
107
  var get = (object, path, defaultValue) => {
93
108
  if (!path || !isObject(object)) {
94
109
  return defaultValue;
@@ -229,11 +244,20 @@ function deepEqual(object1, object2, visited = new WeakMap()) {
229
244
  return true;
230
245
  }
231
246
 
232
- function useResyncOnReconnect() {
247
+ function useResyncOnReconnect(getInitialValue) {
233
248
  const _connected = React.useRef(false);
249
+ const _initialized = React.useRef(false);
234
250
  const _prevValue = React.useRef(undefined);
251
+ const _renderCount = React.useRef(0);
252
+ _renderCount.current++;
253
+ if (!_initialized.current && getInitialValue) {
254
+ _initialized.current = true;
255
+ _prevValue.current = cloneObject(getInitialValue());
256
+ }
235
257
  const resyncIfNeeded = React.useCallback((enabled, getCurrentValue, setValue) => {
236
- if (enabled && _connected.current) {
258
+ if (enabled &&
259
+ (_connected.current ||
260
+ (_initialized.current && _renderCount.current > 1))) {
237
261
  const currentValue = getCurrentValue();
238
262
  if (!deepEqual(_prevValue.current, currentValue)) {
239
263
  setValue(currentValue);
@@ -282,10 +306,11 @@ function useResyncOnReconnect() {
282
306
  function useFormState(props) {
283
307
  const formControl = useFormControlContext();
284
308
  const { control = formControl, disabled, name, exact } = props || {};
285
- const [formState, updateFormState] = React.useState(() => ({
309
+ const getCurrentFormState = () => ({
286
310
  ...control._formState,
287
311
  defaultValues: control._defaultValues,
288
- }));
312
+ });
313
+ const [formState, updateFormState] = React.useState(getCurrentFormState);
289
314
  const _localProxyFormState = React.useRef({
290
315
  isDirty: false,
291
316
  isLoading: false,
@@ -296,12 +321,8 @@ function useFormState(props) {
296
321
  isValid: false,
297
322
  errors: false,
298
323
  });
299
- const { resyncIfNeeded, snapshot } = useResyncOnReconnect();
324
+ const { resyncIfNeeded, snapshot } = useResyncOnReconnect(getCurrentFormState);
300
325
  useIsomorphicLayoutEffect(() => {
301
- const getCurrentFormState = () => ({
302
- ...control._formState,
303
- defaultValues: control._defaultValues,
304
- });
305
326
  resyncIfNeeded(!disabled, getCurrentFormState, updateFormState);
306
327
  const unsubscribe = control._subscribe({
307
328
  name,
@@ -336,7 +357,7 @@ var generateWatchOutput = (names, _names, formValues, isGlobal, defaultValue) =>
336
357
  }
337
358
  if (Array.isArray(names)) {
338
359
  return names.map((fieldName) => (isGlobal && _names.watch.add(fieldName),
339
- get(formValues, fieldName)));
360
+ get(formValues, fieldName, get(defaultValue, fieldName))));
340
361
  }
341
362
  isGlobal && (_names.watchAll = true);
342
363
  return formValues;
@@ -367,10 +388,11 @@ function useWatch(props) {
367
388
  const _prevControl = React.useRef(control);
368
389
  const _prevName = React.useRef(name);
369
390
  _compute.current = compute;
370
- const [value, updateValue] = React.useState(() => {
391
+ const getInitialOutput = () => {
371
392
  const defaultValue = control._getWatch(name, _defaultValue.current);
372
393
  return _compute.current ? _compute.current(defaultValue) : defaultValue;
373
- });
394
+ };
395
+ const [value, updateValue] = React.useState(getInitialOutput);
374
396
  const getCurrentOutput = React.useCallback((values) => {
375
397
  const formValues = generateWatchOutput(name, control._names, values || control._formValues, false, _defaultValue.current);
376
398
  return _compute.current ? _compute.current(formValues) : formValues;
@@ -390,7 +412,7 @@ function useWatch(props) {
390
412
  }
391
413
  }
392
414
  }, [control._formValues, control._names, disabled, name]);
393
- const { resyncIfNeeded, snapshot } = useResyncOnReconnect();
415
+ const { resyncIfNeeded, snapshot } = useResyncOnReconnect(getInitialOutput);
394
416
  const _refreshValue = React.useRef(refreshValue);
395
417
  _refreshValue.current = refreshValue;
396
418
  const _getCurrentOutput = React.useRef(getCurrentOutput);
@@ -471,7 +493,12 @@ function useController(props) {
471
493
  const formControl = useFormControlContext();
472
494
  const { name, disabled, control = formControl, shouldUnregister, defaultValue, exact = true, } = props;
473
495
  const isArrayField = isNameInFieldArray(control._names.array, name);
474
- const defaultValueMemo = React.useMemo(() => get(control._formValues, name, get(control._defaultValues, name, defaultValue)), [control, name, defaultValue]);
496
+ const defaultValueMemo = React.useMemo(() => {
497
+ const resolved = get(control._formValues, name, get(control._defaultValues, name, defaultValue));
498
+ return isUndefined(resolved)
499
+ ? getNullAncestorValue(control, name)
500
+ : resolved;
501
+ }, [control, name, defaultValue]);
475
502
  const value = useWatch({
476
503
  control,
477
504
  name,
@@ -2617,7 +2644,7 @@ function createFormControl(props = {}) {
2617
2644
  }
2618
2645
  for (const fieldKey in value) {
2619
2646
  if (!value.hasOwnProperty(fieldKey)) {
2620
- return;
2647
+ continue;
2621
2648
  }
2622
2649
  const fieldValue = value[fieldKey];
2623
2650
  const fieldName = name + '.' + fieldKey;
@@ -2738,7 +2765,7 @@ function createFormControl(props = {}) {
2738
2765
  const shouldSkipValidation = hasNoValidationEffect ||
2739
2766
  skipValidation(isBlurEvent, get(_formState.touchedFields, name), _formState.isSubmitted, _validationModeAfterSubmit, _validationModeBeforeSubmit);
2740
2767
  const watched = isWatched(name, _names, isBlurEvent);
2741
- set(_formValues, name, fieldValue);
2768
+ set(_formValues, name, cloneObject(fieldValue));
2742
2769
  if (isBlurEvent) {
2743
2770
  if (!target || !target.readOnly) {
2744
2771
  field._f.onBlur && field._f.onBlur(event);
@@ -2870,6 +2897,12 @@ function createFormControl(props = {}) {
2870
2897
  delete delayErrorCallbacks[name];
2871
2898
  }
2872
2899
  }
2900
+ if (options.shouldTouch) {
2901
+ for (const fieldName of name ? fieldNames : _names.mount) {
2902
+ !_names.array.has(fieldName) &&
2903
+ set(_formState.touchedFields, fieldName, true);
2904
+ }
2905
+ }
2873
2906
  _subjects.state.next({
2874
2907
  ...(!isString(name) ||
2875
2908
  ((_proxyFormState.isValid || _proxySubscribeFormState.isValid) &&
@@ -2877,6 +2910,10 @@ function createFormControl(props = {}) {
2877
2910
  ? {}
2878
2911
  : { name }),
2879
2912
  ...(_options.resolver || !name ? { isValid } : {}),
2913
+ ...(options.shouldTouch &&
2914
+ (_proxyFormState.touchedFields || _proxySubscribeFormState.touchedFields)
2915
+ ? { touchedFields: _formState.touchedFields }
2916
+ : {}),
2880
2917
  errors: _formState.errors,
2881
2918
  });
2882
2919
  options.shouldFocus &&
@@ -3244,7 +3281,7 @@ function createFormControl(props = {}) {
3244
3281
  }
3245
3282
  if (!options.keepError) {
3246
3283
  unset(_formState.errors, name);
3247
- _proxyFormState.isValid && _setValid();
3284
+ _setValid();
3248
3285
  }
3249
3286
  _subjects.state.next({ ..._formState });
3250
3287
  }