Form Validation and Reset with React Hook Form

Form validation is a tedious task. React forms are no exception!

The react hook form package provides a simple abstraction layer to delegate the validation tasks. We can validate form fields with very few lines of code!

Install the package with the following code

npm install react-hook-form@latest

Then import the package to the top of the file containing the form

import {useForm} from 'react-hook-form'

Then we have to destruct the useForm object

const { register, handleSubmit, reset, formState: { errors } } = useForm()

De-structuring can be a bit confusing at first, but in short it is a neat way to extract only needed elements from an object.

So, register, handleSubmit, reset, formState – all belong to useForm hook and we will be using them shortly.

Let us create the form –

        <form>
            <div className="form-group col-md-3 pt-4 pb-4">
                <input 
                    type="text" 
                    name="slug"
                    className="form-control"
                />
            </div>
            <div className="form-group col-md-3 pt-4 pb-4">
                <input 
                    type="text" 
                    name="title"
                    className="form-control"
                />
            </div>
            <div className="form-group"> 
                <button 
                    type="submit"
                    className="btn btn-primary">Submit</button>
            </div>
        </form>

So, we have a form with two input fields – slug and title.

By the way, I am using bootstrap to style the form, so bootstrap also needs to be installed and imported like this –

npm install react-bootstrap bootstrap

Then the bootstrap file needs to be imported like this –

import 'bootstrap/dist/css/bootstrap.min.css'

The import should be done on your App.js file, OR on the entry point (usually index.js) file.

The className parameter takes the name of bootstrap CSS class(es).

We will use controlled inputs. So the form fields will be written in the following way –

const [fields, setFields] = useState({slug: '', title: ''})

We are assigning initial values of slug and title as empty strings with {slug: ”, title: ”}. The useState hook now needs to be imported from react package.

import {useState} from 'react'

The updated form now looks like this –

import 'bootstrap/dist/css/bootstrap.min.css'
import { useState } from 'react'

export default function App() {

const [fields, setFields] = useState({slug: '', title: ''})

function onChange(event)
{
    setFields({...fields, [event.target.name]:event.target.value})
}

function onSubmit(event)
{
    console.log(fields)
}

return (

    <div className="App">
		<div className='col-md-3 p-5'>
			<form onSubmit={onSubmit}>
			<div className='form-group pt-5'>
				<input 
					className='form-control' 
					value={fields.slug}
					onChange={onChange}
					name="slug" />
			</div>
			<div className='form-group pt-3 pb-3'>
				<input 
					className='form-control' 
					value={fields.title}
					onChange={onChange}
					name="title" />
			</div>
			<div className='form-group'>
				<button 
					type='submit'
					className='btn btn-primary'
					>Submit</button>
			</div>
			</form>
		</div>
    </div>
  )

}

The values of the form fields take from the fields object whose initial value is (set using useState) –

useState({ slug: '', title: '' })

When we type something to an input field the onChange event is triggered which in turn calls the onChange function –

function onChange(event)
{
    setFields({...fields, [event.target.name]:event.target.value})
}

The onChange function is passed an event parameter which takes the name of the target input field in event.target.name and assigns it the value event.target.value with setFields.

[The spread notation , accesses the fields object and changes its event.target.name (which can be slug or title ) field value to event.target.value (the typed value)]

On submitting the form the onSubmit handler function is called which just displays the fields current values with console.log(fields) !

Start your development environment with –

npm run start

And test the form. It should show the fields value in console on form submit.

We are not going into the depths of creating an API endpoint and sending the form input values. Instead we will test the react-hook-form authentication package now.

The packages needs to be imported first –

import {useForm} from 'react-hook-form'

The packages exposes an useForm hook with several properties that we can use in our form.

The updated form now looks like this –

import 'bootstrap/dist/css/bootstrap.min.css'
import { useState, useEffect } from 'react'
import {useForm} from 'react-hook-form'

export default function App() {

const initFields = {slug: '', title: '')
const [fields, setFields] = useState(initFields)

const { register, handleSubmit, reset, formState: { errors } } = useForm()

function onChange(event)
{
    setFields({...fields, [event.target.name]:event.target.value})
}

function onSubmit({}, event)
{
    event.preventDefault()
    console.log(fields)
    // work with API endpoint here
    // to send data to database or do something else
    setFields(initFields)   
}

 useEffect(() =>{
        reset(fields)
 }, [fields])

return (

    <div className="App">
		<div className='col-md-3 p-5'>
			<form onSubmit={handleSubmit(onSubmit)}>
			<div className='form-group pt-5'>
				<input 
					className='form-control' 
                                        {...register('slug', {required: true})}
					value={fields.slug}
					onChange={onChange}
					name="slug" />
                                     {errors.slug && <span>Slug is required</span>}
			</div>
			<div className='form-group pt-3 pb-3'>
				<input 
					className='form-control' 
					value={fields.title}
                                        {...register('title', {required: true})}
					onChange={onChange}
					name="title" />
			</div>
			<div className='form-group'>
				<button 
					type='submit'
					className='btn btn-primary'
					>Submit</button>
			</div>
			</form>
		</div>
    </div>
  )

}

The react-hook-form package exposes the useForm hook with properties register, handleSubmit, reset and formState.

The register property is used to register form elements for authentication, the handleSubmit property is used on the onSubmit function to capture the validation rules before the values are through for API endpoints.

The reset property is used to reset the form and the formState is used for validation errors.

When the form is submitted, handleSubmit checks for any validation errors. If there are any errors, then those are passed to errors property of formState. We can display them with errors.field_name syntax as illustrated above.

The onSubmit function now gets two parameters, data from the validation checks and an event parameter. We do not need the data, so we will use an empty object with { }.

The form field values on this function are taken from the fields object which has all the values of the form input fields.

This data is then passed to an API endpoint to get stored or do something else!

After the submission completes, we are setting the fields value to empty strings with –

setFields(initFields)

This is done to empty the form fields. Then we also call useEffect to reset the form –

 useEffect(() =>{
        reset(fields)
 }, [fields])

Remember that reset should not be used in onSubmit function. Always use it with useEffect and pass the fields as parameter.

Doing so will reset the fields to original empty strings. Else the state will persist even after you submit the form and you may get errors in subsequent calls!