Integrate React Hook Forms with Existing Form Components
Niya Panamdanam
Posted on June 3, 2021
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
I found the best part of the library is the ease of integrating it into existing legacy code with minimal adjustments.
The 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
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.
Posted on June 3, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.