Gotey
Gotey

Reputation: 639

react-hook-form not triggering onSubmit when using Controller

I have the following form:

      <form onSubmit={handleSubmit(onSubmit)}>
        <Controller
          name="voucherPrice"
          control={control}
          defaultValue={false}
          rules={{ required: true }}
          render={({ field: { onChange, value, ref } }) => (
            <Input {...field} onChange={(ev) => handlePriceInputChange(ev)} value={price} type="number" innerRef={ref} />
          )}
        />

        <p>{errors.voucherPrice?.message}</p>

        <Button
          variant="contained"
          sx={{ mt: 1, mr: 1 }}
          type="submit"
        >
          {"Continue"}
        </Button>
      </form>

and with this configuration:

function PriceSelection(props) {
  const {
    register,
    handleSubmit,
    control,
    formState: { errors },
  } = useForm({
    resolver: yupResolver(schema),
  });
  const onSubmit = (data) => {
    console.log("does not work?", data);
  };

  const classes = useStylesPriceSelection();
  const [selected, setSelected] = useState(false);
  const [price, setPrice] = useState("");


  const handlePriceInputChange = (ev) => {
    console.log("change", price);
    setPrice(parseInt(ev.target.value));
  };



The function onSubmit does not trigger when I press the submit button. Also I would like the input field to be filled by default by the state price and its value to be sent with the parameter data on the function onSubmit when I push the submit button.

Upvotes: 2

Views: 5365

Answers (1)

knoefel
knoefel

Reputation: 6989

You are mixing useState with react-hook-form and are not updating react-hook-form's internal form state. You don't need to declare a useState for your field.

In your example you are destructering onChange from the field object of <Controller /> but you are never using it for your <Input /> component. Therefore react-hook-form can't update it's form state. As you set your field to be required the onSubmit callback won't get triggered because react-hook-form will never receive an update or value for it.

The correct way would be:

<Controller
  name="voucherPrice"
  control={control}
  rules={{ required: true }}
  render={({ field: { ref, onChange, value, ...field } }) => (
    <Input {...field} onChange={onChange} value={value} type="number" innerRef={ref} />
  )}
/>

Or even shorter:

<Controller
  name="voucherPrice"
  control={control}
  rules={{ required: true }}
  render={({ field: { ref, ...field } }) => (
    <Input {...field} type="number" innerRef={ref} />
  )}
/>

UPDATE

If you need to have access to the value outside of the <Controller />, you should use react-hook-form's watch method. This will allow you to subscribe to the latest value of the voucherPrice field and use it inside your component -> Docs

If you want to set or update the value programmatically you can use the setValue method from react-hook-form -> Docs

const { control, handleSubmit, watch, setValue } = useForm();

const voucherPrice = watch("voucherPrice");

const onButtonClick = () => {
  setValue("voucherPrice", <newValue>);
}

If you really need to have a separate useState for your value and want to update it additionally to your react-hook-form field update, you could do the following:

<Controller
  name="voucherPrice"
  control={control}
  rules={{ required: true }}
  render={({ field: { ref, onChange, value, ...field } }) => (
    <Input
      {...field}
      onChange={(v) => {
        onChange(v);
        handlePriceInputChange(v);
      }}
      value={value}
      type="number"
      innerRef={ref}
    />
  )}
/>

But i would suggest to use the react-hook-form only solution, as it has all the functionality you need to manage your form state.

Upvotes: 3

Related Questions