DamiToma
DamiToma

Reputation: 1106

How to trigger re-render with `setValue` using react-hook-form?

I have a simple form with a select field, it's react-hook-form for validation and everything. There's a Controller which renders a Material UI Select. I would like to set the value of such select using setValue from outside the component (in the root of the form, where all controls reside).
This is the piece of code I'm using (slightly simplified not to waste too much of your time)

type Props = {
  name: string;
  control: Control<any>;
  options: SelectOptions[];
};
const Select: FunctionComponent<Props> = ({
  name,
  control,
  options,
}) => (
  <Controller
    control={control}
    name={name}
    render={({ field: { onChange, value } }) => {
      return (
        <FormControl>
          <MuiSelect onChange={onChange}>
            {options.map((o) => (
              <MuiSelectItem key={o.key} value={o.value}>{o.label}</MuiSelectItem>
            ))}
          </MuiSelect>
        </FormControl>
      )
    }}
  />
);

As far as changing the value of the select, setValue works magically. When I feed a new value, it works as intended. The problem (I guess) is the component is not re-rendered, so the old value is still shown. I'm not sure how to fix this thing and docs did not help a lot. Any help is much appreciated, thanks!

Upvotes: 18

Views: 45238

Answers (3)

cr4z
cr4z

Reputation: 581

Here's a snippet from my codebase. In my case, I simply added a ternary operator to MUI's value prop. My previous code value={value} became value={value ? value : ""} and it sufficed to force MUI to rerender on each value change. I'm not fully certain on why this works, but if anyone knows, I would love to be enlightened.

 <Controller
      control={props.control}
      name={props.name}
      render={({ field: { onChange, value } }) => {
        return (
          <TextField
            size="small"
            fullWidth
            value={value ? value : ""}
            type="text"
            label={props.label}
            {...props.register(props.name)}
            onChange={(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
              onChange(e);
              if (props.onAfterChange) {
                props.onAfterChange(e);
              }
            }}
          />
        );
      }}
    />

Hope this helps!

Upvotes: 1

Sanka Sanjeeva
Sanka Sanjeeva

Reputation: 3520

As @knoefel said I tried setting the defaultValue="" but doesn't work for me(maybe because I am using FormProvider). So what I did is use watch instead of value

<Controller
  name='name'
  control={control}
  render={({ field: { onChange } }) => (
    <Select
      dropdownValues={dropdownValues}
      value={watch('name')}
      onChange={onChange}
    />
  )}
/>

Upvotes: 17

knoefel
knoefel

Reputation: 6949

I think you just forgot to set the value prop of <Controller /> to your <MuiSelect />. You also have to set a defaultValue for your <Controller /> field, either via the defaultValue prop or via useForm.

<Controller
  control={control}
  name={name}
  defaultValue=""
  render={({ field: { onChange, value } }) => {
    return (
      <FormControl>
        <MuiSelect onChange={onChange} value={value}>
          {options.map((o) => (
            <MuiSelectItem key={o.key} value={o.value}>{o.label}</MuiSelectItem>
          ))}
        </MuiSelect>
      </FormControl>
    )
  }}
/>

Edit BasicSelect Material Demo (forked)

Upvotes: 6

Related Questions