Mehnaz Khan
Mehnaz Khan

Reputation: 641

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

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

Upvotes: 64

Views: 186877

Answers (10)

pouyada
pouyada

Reputation: 377

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

Upvotes: 1

Francis Leigh
Francis Leigh

Reputation: 1960

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);
          }}
        />
      )}
    />
  </>;
}

Upvotes: 0

Jason Ching
Jason Ching

Reputation: 2239

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

Upvotes: 1

anunaki
anunaki

Reputation: 2324

You can use react-hook-form control

  <Controller
  render={({ field }) => <input onChange={event=>{
      handleImageUpload(event);
      field.onChange(event);
    }} />}
  name="image"
  control={control}
/>

Upvotes: 6

Joris
Joris

Reputation: 2796

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.

Upvotes: 47

Nirjal Mahat
Nirjal Mahat

Reputation: 447

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!!

Upvotes: 4

Bill
Bill

Reputation: 19338

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) => {},
  })}
/>

Upvotes: 133

Christophe Destombes
Christophe Destombes

Reputation: 71

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);
       }} />

Upvotes: 7

Dmitry Ermichev
Dmitry Ermichev

Reputation: 423

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}/>

Upvotes: 1

GGrassiant
GGrassiant

Reputation: 119

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.

Upvotes: 0

Related Questions