react-hook-form: Inconsistent `watch()` behaviour on complex field names

Describe the bug

When having default values with complex fields the initial watch()-call gives the default values rather than the flattened version.

To Reproduce Steps to reproduce the behavior:

  1. Go to https://codesandbox.io/s/react-hook-form-watch-issue-hbgwl
  2. Observe “Data from watch():” below the Submit-button
  3. Edit Complex field

Codesandbox link

https://codesandbox.io/s/react-hook-form-watch-issue-hbgwl

Expected behavior

Both initial watch()-call and following watch()-calls should produce Record<string, any> rather than whatever default form values you put in.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 1
  • Comments: 23 (10 by maintainers)

Commits related to this issue

Most upvoted comments

it’s much cheaper to run getValues than watch

I’ll see if I can make it more performant w/o context and report back

hey @KATT no worries. I would prefer without using FormContext for your senario, try to compose or pass down props instead Context if possible 😃

Merry Xmas

Thank you!! It has acceptable perf now… still triggers unnecessary visit to the render as I use context in it, but I’ll see what I can think of later this week to improve it further.

Merry xmas 🎅

I can refactor my code to rely on { nested: true }, that wasn’t an option before V4.

A bit of background –

I’m using react-hook-form over formik in the first place because I have a form with > 100 inputs (a table of data you can bulk update).

In each row I have columns that need to be conditional based on others, i.e. a checkbox should display another input.

To achieve this I have a context provider in my form that calls watch() once (calling it in each individual row was expensive).

This is how a cell in my big table looks like:



const ResultInputs: React.FC<{
  disabled: boolean;
  namePrefix;
  onChangeResults: TextFieldProps['onChange'];
  register: UseFormInstance['register'];
}> = ({ disabled, namePrefix, onChangeResults, register }) => {
  const { values } = useBulkUpdateContext();
  const enabled = values[`${namePrefix}.enabled`];
  // Avoid re-render except if stuff change
  return useMemo(() => {
    console.log('rerender', `${namePrefix}.enabled`, enabled);
    const checkbox = false ? (
      // This doesn't work for some reason (prob issue in material UI)
      <FormControlLabel
        control={<Checkbox color="primary" />}
        label="Enabled"
        disabled={disabled}
        inputRef={register}
        name={`${namePrefix}.enabled`}
      />
    ) : (
      // tmp workaround
      <label>
        <input
          type="checkbox"
          disabled={disabled}
          ref={register}
          name={`${namePrefix}.enabled`}
        />{' '}
        Played
      </label>
    );

    return (
      <>
        {checkbox}
        {/* 
          Rather than doing `display: 'none'` here I previously did 
          `{enabled && <TextField ..}` but that caused all rows to re-render
        */}
        <div style={{ display: enabled ? 'inline-block' : 'none' }}>
          <TextField
            name={`${namePrefix}.value`}
            type="text"
            inputRef={register}
            disabled={disabled}
            label="Value"
            onChange={onChangeResults}
          />
        </div>
      </>
    );
  }, [disabled, namePrefix, onChangeResults, enabled, register]);
};

actually @KATT you want to give a try on the RC version, then we can go from there:

react-hook-form@4.0.0-rc3