How to submit react form fields on onChange rather than on submit using React-Hook-Form library

Viewed 11793

I have started using new React-Hook-form library and wanted to submit my input fields on Change rather than on Submit. Also, I am planning to use debounce from Lodash to avoid re-rendering

This is what I have tried so far:

import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import { useForm } from "react-hook-form";

import "./styles.css";

export default function App() {
  const { register, handleSubmit, setValue } = useForm();
 

  useEffect(() => {
    register({ name: "firstName" }, { required: true });
  }, [register]);

  return (
    <form>
      <input
        name="firstName"
        onChange={(e) => {
          setValue("firstName", e.target.value);
          handleSubmit((data) => console.log("data", data));
        }}
      />
    </form>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
6 Answers

I found the simplest way to be:

const { handleSubmit, watch } = useForm();
const onSubmit = (data) => console.log(data)

useEffect(() => {
    const subscription = watch(handleSubmit(onSubmit));
    return () => subscription.unsubscribe();
}, [handleSubmit, watch]);

If you log your implementation of handleSubmit inside onChange, you will notice that it returns a function

enter image description here

Try to invoke the returned function and it should submit.

onChange={(e) => {
    setValue("firstName", e.target.value);
    handleSubmit((data) => console.log("data", data))();
}}

I've also recently written my version of "user is done with typing, then submit" implementation for input fields - check it out: https://stackoverflow.com/a/63419790/8680805

From your example it looks like you only want to retrieve (all) the values after input change. Not sure if this will help you, but you can use the getValues function from the useForm hook.

const Filters: FC<TProps> = ({ filters, setActiveFilter }) => {
  const { register, getValues } = useForm();

  const handleChange = () => {
    const values = getValues();
    // Do something with values, in my case I have used the 'setActiveFilter' function above.
  };

  return (
    <form>
      {filters.map((filter) => (
        <fieldset key={filter.id}>
          <legend>{filter.label}</legend>
          {filter.options.map((option) => (
            <label htmlFor={option.value} key={option.id}>
              <input
                type="checkbox"
                id={option.value}
                name={filter.label}
                value={option.id}
                ref={register}
                onChange={handleChange}
              />
              {option.value}
            </label>
          ))}
        </fieldset>
      ))}
    </form>
  );
};

why do you want to submit it onChange?

but if you really want to do it, this should work:

import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import { useForm, useWatch } from "react-hook-form";

import "./styles.css";

export default function App() {
  const { register, control, handleSubmit } = useForm()
  const firstName = useWatch({ control, name: 'firstName' })

  useEffect(() => {
    if(firstName && firstName.length > 0) {
      handleSubmit(data => console.log("data", data))()
    }
  }, [firstName, handleSubmit])

  return (
    <form>
      <input ref={register({ required: true })} name="firstName" />
    </form>
  )
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

in react hook forms watch is the alternative way for onChange, simply do this:

const handleSubmit = (data) => {
 console.log(data)
}
    const {watch} = useForm();
    
          watch((data, { name }) => {
            if (name === "firstName") handleSubmit(data);
          });

Alex answer not working in my case since I need a small delay before submiting form (when user stopped typing)

Here is my solution

const { handleSubmit, watch } = useForm(); const onSubmit = (data) => console.log(data);

useEffect(() => {
 const subscription = watch(() => {
   if (timer) clearTimeout(timer);

   timer = setTimeout(() => {
     handleSubmit(onSubmit)();
   }, 1000);
 });
 return () => subscription.unsubscribe();
}, [handleSubmit, watch]);
Related