react-hook-form: form is not re-rendered when using setValue to update a field that was manually registered

Describe the bug When registering fields manually, the form does not re-render after calling setValue.

To Reproduce

const MyForm = () => {
  const { register, unregister, getValues, setValue } = useForm({
    defaultValues: { email: '' }
  });

  useEffect(() => {
    register({ name: 'email' });
    return () => unregister({ name: 'email' });
  }, [register, unregister]);

  /* getValues does not work because setValue does not trigger a re-render */
  const values = getValues();

  const onChange = e => setValue(e.target.name, e.target.value, true);

  return <input type="text" name="email" value={values.email} onChange={onChange} />;
};

Expected behavior Explicitly calling setValue should trigger a re-render

The usual use-case would be to simply pass register into the input’s ref. However, manually registering fields is still required for components that don’t expose a ref prop. The above code snippet is just a contrived example to show that manually registering a field does not work as intended.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 21
  • Comments: 43 (33 by maintainers)

Commits related to this issue

Most upvoted comments

actually, that’s not the correct usage… setValue shouldn’t trigger re-render unless there is an error for the field (react hook form is more towards uncontrolled component). for the controlled component, you should use useState and setValue.

setValue should be used to update the uncontrolled components.

For anyone who is registering fields manually like Autocomplete from Material UI + React Hook Form you can provide options as a 3rd argument of setValue method.

It should work like this


const { setValue } = useForm()

<Autocomplete
   ...
   onChange={event, option => setValue('fieldName', option.value, { shouldValidate: true }}
   ...
/>

use watch() instead of getValues()

hey @petercpwong just use watch()! i think it should work!

We should be able to use watch on a specific value const watchfirstName = watch("firstName", props.firstName); with a Controller.

This should trigger a re-render because that’s what’s expected with a watch’like function 😃

This allows to bypass a redundant pattern when useState implemented locally triggers re-rendering of form which then can take into account the change of value for whatever side effect.

@balupton, I think that question needs an update because we now have Controller for that.

@bluebill1049 I recently started using react and react-hook-forms, I am using a controlled with a third part input component, when user move between tabs context updates state, but when user comes back to initial tab the data need to be presented, how can I achieve this , didn’t want to use defaultValue as this is not triggering validations.

you can set shouldUnregsiter: false

I believe there is no reason to use a manual register since we already have Controller and soon useController that you can use. If you are working with conditional fields, shouldUnregister is your friend.

@bluebill1049 I tried with shouldUnregsiter: false still I don’t see validations triggering.

I have also tried below, but this is triggering validations on initial load of empty page


 useEffect(() => {
   console.log("trigger validation");

   trigger();
 }, [trigger]);

Thanks for jet speed reply

can you share a simple codesandbox for what you try to achieve? maybe i am missing the context.

Also, if you could provide a codesandbox that would be helpful.

Let us know your thoughts after you tried both. We can re-open this one if the above solutions doesn’t solve your issue.

You can’t go wrong with either of the two because what you’re doing is just want Controller is doing under the hood.

thanks @JeromeDeLeon for answering this 😃

@balupton and @petercpwong, have you both tried updating the react-hook-form and use Controller instead?

Here’s an example of using Controller for UI lib that doesn’t support ref.

at the same time, let me think about better* solutions…

I would like to take a look at such component which only supports controlled/props

There are actually quite a few components that only support controlled props (whether that’s a good design choice is another question).

One notable example: https://polaris.shopify.com/components/forms/text-field

The above component doesn’t expose a ref prop and can only be interfaced with onChange and setting of the value prop.

I agree with extra useState is bad and I am not supporting that (i was not fully awake in the morning 😃, but why getValues during render.

const MyForm = () => {
  const { register, unregister, getValues, setValue } = useForm({
    defaultValues: { email: '' }
  });

  useEffect(() => {
    register({ name: 'email' });
  }, [register]); // u don't need unregister here which i should document it in the site

  const onChange = e => setValue(e.target.name, e.target.value, true);

  return <BlackboxComponent onChange={onChange} />;
};

why not just remove value? let the component render the value itself.