Integrate React Hook Forms with Existing Form Components
Tags: codequality, react, javascript, tutorial
Published: 2021-06-03 03:09:09

React Hook Form is a form handler that handles validation, error, etc using hooks. The library also has great integration with Typescript. You can read more about the it here: [React Hook Form](https://react-hook-form.com/)

I found the best part of the library is the ease of integrating it into existing legacy code with minimal adjustments.

The *[Get Started](https://react-hook-form.com/get-started)* documentation will walk you through how to use the `useForm` hook and `register` option for form elements. This is a great option if you are not using third-party form elements like React Select, Antd, Material UI, or legacy custom form element components.

For our sample case, I have the following `UserProfile.jsx` component, with a custom input component.

###UserProfile.jsx
```
export const UserProfile = () =>{
 
 const [userName, setUserName] = useState('')
 const [email, setEmail] = useState('')

const onSubmit = async()=> {
  await axios.post('/user',{userName, email})
    .then(()=>console.log('Success'))
    .catch((e)=> console.error(e))
}

 return (
  <div>
    <CustomInput 
      type='text'
      label='User Name'
      name='userName' 
      value={userName} 
      onChange={setUserName}
    />
    <CustomInput 
      type='text'
      label='Email'
      name='email' 
      value={email} 
      onChange={setEmail}
    />
    <button onClick={onSubmit}>
     Submit
    </button>
  </div>
 )
}
```

###The `CustomInput.jsx` component:
```
export const CustomInput = (
  {
    name,
    type = 'text',
    label,
    disabled = false,
    value,
    onChange,
  },
) => {
return (
    <>
        <label htmlFor={name}>{label}</labe>
        <input
          value={value}
          onChange={(v) => onChange(v.target.value)}
          type={type}
          disabled={disabled}
        />
    </>
  )
}
```

In the cases of the third-party libraries, React Hook Form recommends we use the `Controller` component to wrap the third-party component. You can read more about it here: [Controller](https://react-hook-form.com/api/usecontroller/controller)

We can use the same `Controller` functionality through the `useController` hook to update `CustomInput.jsx` component.

###Updated `CustomInput.jsx`
```
export const CustomInput = (
  {
    name,
    type = 'text',
    label,
    disabled = false,
    controller,
    rules /**A way to set input validation**/
  },
) => {

const { field } = useController({
      name, /**This is the unique identifier used by React Hook Form**/
      rules,
      control,
    })

return (
    <>
      <label htmlFor={name}>{label}</label>
      <div>
        <input
           {...field} /**this allows React Hook Form to handle the value, onChange and other form functionalities**/
          type={type}
          disabled={disabled}
        />
      </div>
    </>
  )
}
```

Every time the `CustomInput` component is used it will require the `controller` prop. Now we modify the parent `UserProfile.jsx` component to use `useForm` hook and pass in the `controller`.

###Use `useForm` Hook
First we remove the `useState` hook and use the `useForm` hook.
```
const {controller, handleSubmit} = useForm({
  defaultValues:{
    userName:'',
    email: '',
  }
 })
```

###Update the props
Then update the props passed into the `CustomInput` Component.
```
<CustomInput 
      type='text'
      label='User Name'
      name='userName' 
      controller={controller}
      rules={{ required: true}} /** passing in the validation rule**/
    />
```

###Update the button
Next we need to update the `button` to trigger the `handleSubmit` from the `useForm` hooks. This will allow us to use the validated form data in our `onSubmit` function.
```
<button 
  onClick={handleSubmit(
    (data)=>onSubmit(data)
   )}
>
  Submit
</button>
```

###Update onSubmit
Last we need to update the `onSubmit` function to use the correct `data`.
```
const onSubmit = async(data) => {
  await axios.post('/user',{
      userName:data.userName, 
      email:data.email
    })
    .then(()=>console.log('Success'))
    .catch((e)=> console.error(e))
}
```

Our final `UserProfile.jsx` component looks as follows:
```
export const UserProfile = () =>{
 
/**Removed state and replaced it with useForm**/
 const {controller, handleSubmit} = useForm({
  defaultValues:{
    userName:'',
    email: '',
  }
 })

/**Updated the submit function to get validated data from the useForm hook**/
const onSubmit = async(data) => {
  await axios.post('/user',{
      userName:data.userName, 
      email:data.email
    })
    .then(()=>console.log('Success'))
    .catch((e)=> console.error(e))
}

 return (
  <div>
    <CustomInput 
      type='text'
      label='User Name'
      name='userName' 
      controller={controller}
      rules={{ required: true}} /** passing in the validation rule**/
    />
    <CustomInput 
      type='text'
      label='Email'
      name='email' 
      controller={controller}
      rules={{ required: true}}
    />
    <button 
      onClick={handleSubmit((data)=>onSubmit(data))} /**the data passes in the validated input values to the submit function**/
    >
     Submit
    </button>
  </div>
 )
}
```

With these changes we can continue to use existing form elements, and integrate the powerful tools of React Hook Forms with minimal changes.