React hook form: How to can I use onChange on React Hook Form Version 7.0
Asked Answered
L

10

60

Previously I used to write like this:

<input className="form-control" name="productImage" type='file' onChange={handleImageUpload} ref={register({ required: true })} />

After the update I have to write like this:

<input className="form-control" type="file" {...register('productImage', { required: true })} />

How do I use onChange={handleImageUpload} on the updated version of React Hook Form? Here is the migration docs

Lauricelaurie answered 3/4, 2021 at 21:42 Comment(8)
You don't have to make any changes to the onChange prop in react-hook-form v7.0.0.Coppery
How can I call handleImageUpload() by using onChange={handleImageUpload) like the first line of code I have shown?Lauricelaurie
onChange={handleImageUpload) should work. Can you share a CodeSandbox if you're running into any issues?Coppery
Here's my CodeSandbox and the onChange={handleImageUpload} doesn't work in my code.Lauricelaurie
The handleImageUpload function does get called when you upload a file.Coppery
I just uninstalled the version 7.0 and installed 6.13.1. On the older version, it works for me just fine but for version 7.0 it didn't work. I really don't know what went wrong. But Thank You for helping me out. Truly appreciate your concern.Lauricelaurie
I've been dealing with this issue, you can call register and store the object to a variable, and then manually set those values. It sucks but it works.Bes
For those new to this thread, the problem with just adding onChange is that in RHF v7.x calling ...register('name') spreads an object containing {onChange, onBlur, name, ref}, so the custom handler will either be ignored or override RHF functionality. See @Bill's answer for the proper way to deal with this.Pyrrhic
C
124

https://github.com/react-hook-form/react-hook-form/releases/tag/v7.16.0

V7.16.0 has introduced this new API for custom onChange.

<input
  type="text"
  {...register('test', {
    onChange: (e) => {},
    onBlur: (e) => {},
  })}
/>
Cobos answered 5/10, 2021 at 10:29 Comment(7)
This is the correct answer given @Joris's solution would override the change handler spread from {...register('name')} which returns {onChange, onBlur, name, ref}, and this could lead to bugs.Pyrrhic
@Bill, is it possible to achieve the same with the Controller component?Grange
yes @Grange with the controlled component you can intercept the onChange prop.Cobos
onChange: (e) => {console.log(e.target.value)} worked for me !Jacki
My time saver! Thanks!Superstructure
What if we want to use the native onChange but also have it do something else?Biosphere
@Cobos intercepting onChange with Controller interferes with the default behavior (filename no longer appears in input[type=file]), so it looks like those are actually mutually-exclusive.Thornburg
G
47

You just have to move the onChange props after {...register(...)}

const productImageField = register("productImage", { required: true });

return (
    <input
        className="form-control"
        type="file"
        {...productImageField}
        onChange={(e) => {
          productImageField.onChange(e);
          handleImageUpload(e);
     }}
    />
)

(Dec 3 2021) edit: this approach is no longer correct since react-hook-form v7.16.0's changes, see @Bill's answer.

Giselegisella answered 4/4, 2021 at 9:34 Comment(4)
@Bes are you sure this overrides the onChange function? The example above overrides first the onChange function but then invokes it within our overriden onChange function productImageField.onChange(e);. So, what is it that i don't get here?Ladd
@Tanckom I've updated my reply since @ DiamondDrake commentedGiselegisella
@Joris, might be good to mention this in your answer as new viewers may get confused that this is not the correct way :-)Ladd
@Tanckmon is correct, this solution would override the onChange handler spread from {...register('name')} and could lead to bugs. @Bill's answer seems to be the right one here.Pyrrhic
I
7

In register documentation https://react-hook-form.com/api/useform/register, sample exists on Custom onChange, onBlur section :

// onChange got overwrite by register method
<input onChange={handleChange} {...register('test')} />

// register's onChange got overwrite by register method
<input {...register('test')} onChange={handleChange}/>

const firstName = register('firstName', { required: true })
<input 
  onChange={(e) => {
    firstName.onChange(e); // method from hook form register
    handleChange(e); // your method
  }}
  onBlur={firstName.onBlur}
  ref={firstName.ref} 
/>

So for your case :

const productImageRegister = register("productImage", {required: true})
<input className="form-control"
       type="file"
       {...productImageRegister }
       onChange={e => {
           productImageRegister.onChange(e);
           handleImageUpload(e);
       }} />
Insatiable answered 23/7, 2021 at 15:56 Comment(0)
L
6

You can use react-hook-form control

  <Controller
  render={({ field }) => <input onChange={event=>{
      handleImageUpload(event);
      field.onChange(event);
    }} />}
  name="image"
  control={control}
/>
Leucite answered 5/1, 2023 at 10:29 Comment(0)
R
3

Was stuck with the same problem. For me the problem was that my onChange was above the react-hook-form's {...register} and moving it below the register solved the problem for me!!

Rochkind answered 4/3, 2022 at 4:14 Comment(1)
not a solution, this prevents you from typing in the form fieldJosiahjosias
V
1

For me, decoration solution worked

const fieldRegister = register("productImage", {required: true})
const origOnChange = fieldRegister.onChange
fieldRegister.onChange = (e) => {
    const res = origOnChange(e)
    const value = e.target.value
    // do something with value
    return res
}

For field declaration use

<input {...fieldRegister}/>
Vain answered 13/7, 2021 at 11:49 Comment(1)
A little verbose, but this works great for those running on anything earlier than v7.16.0Pyrrhic
U
1

I am using form hook's watch method to capture changes instead of the input's onChange event.

https://react-hook-form.com/api/useform/watch

Uredo answered 23/2, 2023 at 14:0 Comment(0)
P
1

Here is how you can use the onChange together with React hook form and Controller

<Controller
        name="address"
        control={control}
        render={({ field }) => (
            <TextField
                {...field}
                type="text"
                label="address"
                onChange={(e) => {
                    field.onChange(e);
                    yourCustomChangeHandler(e);
                }}
            />
        )}
    />
<Controller
Paradise answered 28/5, 2024 at 14:18 Comment(1)
this solution also works on Select component. thanks!Fuji
S
0

I faced a similar issue recently when migrating to V7. If it can help anybody.

A parent component handling the form was passing down to a wrapper the register function, the wrapper passing it down again to an input that needed debouncing on change.

I called the register formLibraryRef in case I wanted to use a different library later but overall I had to do something like:

const { onChange, ...rest } = formLibraryRef(inputName);

pass the onChange to the function that is itself passed to the native onChange event of the input:

const handleDebouncedChange: (event: React.ChangeEvent<HTMLInputElement>) => void = (
    event: ChangeEvent<HTMLInputElement>,
  ) => {
    onChange(event);
    if (preCallback) {
      preCallback();
    }
    debounceInput(event);
  };

and then pass the rest to the input:

<input
  aria-label={inputName}
  name={inputName}
  data-testid={dataTestId}
  className={`form-control ...${classNames}`}
  id={inputId}
  onChange={handleDebouncedChange}
  onFocus={onFocus}
  placeholder={I18n.t(placeholder)}
  {...rest}
/>

The register section in the docs here: https://react-hook-form.com/migrate-v6-to-v7/ gives a bit more info on how to get the onChange and shows an example for Missing ref.

Surgeonfish answered 22/4, 2021 at 18:41 Comment(0)
S
0

When using a Controller with a control instance I created a handleOnChange method that essentially extends the onChange method passed by the Control.render method.

Like so:

const SomeForm = () => {
  const { control, getValues } = useForm({
    defaultValues: { foo: 1, bar: 2 },
    mode: "all",
    reValidateMode: "onChange",
    shouldUnregister: false,
  });

  const onChangeHandler = useCallback((onChange: (...event: any[]) => void) => {
    return (...event: any[]) => {
      onChange(...event);

      const formAfterChange = getValues();
      // do what you want after the change happens.
    };
  }, [getValues]);


  return <>
    <Controller
      control={control}
      name={"foo"}
      render={({ value, onChange }) => (
        <Input
          value={value}
          onChange={onChangeHandler(onChange)}
        />
      )}
    />

    <Controller
      control={control}
      name={"bar"}
      render={({ value, onChange }) => (
        <Input
          value={value}
          // if you do some logic before calling onChange
          onChange={(e) => {
            let nextValue;
            if ((e.target.value ?? 0) >= 100) {
              nextValue = e.target.value + 10;
            } else {
              nextValue = e.target.value;
            }
            onChangeHandler(onChange)(nextValue);
          }}
        />
      )}
    />
  </>;
}
Siftings answered 16/1, 2024 at 11:21 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.