Form Validation in ReactJS: Build a Reusable Custom Hook for Inputs and Error Handling
Hi there👋,
The advantage of this hook is,
- It is Reusable, so that you can use it anywhere in your website or for other projects.
- You can handle validation easily, You just have to put conditions for input fields you want to validate.
- This hook makes error handling very easy, also you can customize error messages as well.
- Very easy to implement and build
If you prefer to watch video then here is the video tutorial👇
Creating a Basic React App
First of all makes sure you have created a react app using the following command👇
npx create-react-app react-form-component
cd react-form-component
Now start your react app using👇
npm start
Let’s remove the unnecessary code from the App.js
file and create form with 3 inputs and 1 submit button as given in the following code block.
import './App.css';
function App() {
return (
<div className="app">
<form>
<input type="email" name="email" placeholder="E-mail" />
<input type="password" name="password" placeholder="password" />
<input type="text" name="username" placeholder="username" />
<input type="submit" value="Submit" className="submit" />
</form>
</div>
);
}
export default App;
Let’s add some css in the App.css
file.
.app {
text-align: center;
margin-top: 20vh;
}
form{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
input{
margin: 0.5rem 0;
padding: 1rem;
outline: none;
}
.submit{
border: 2px solid #000 !important;
cursor: pointer;
}
Creating The Basic Structure of The Custom Hook
Create Hooks
folder in the src
folder. In this folder create one file called useForm.js
.
To create any custom hook, you can keep different file name but name of the hook should always start from use keyword.
Let’s Write basic snippet in this file as given in the following code block,
const useForm = () => {
return();
}
export default useForm
Implementing State In Custom Hook
In this hook we need two states,
values
: A state to store form values, initially an empty object.errors
: A state to store errors if any, initially an empty object.
let’s create these states,
//Form values
const [values, setValues] = useState({});
//Errors
const [errors, setErrors] = useState({});
Creating a Method to Handle Form Values
Let’s create a method to handle form values and set values state.
//A method to handle form inputs
const handleChange = (event) => {
//To stop default events
event.persist();
let name = event.target.name;
let val = event.target.value;
validate(event,name,val);
//Let's set these values in state
setValues({
...values, //spread operator to store old values
[name]:val,
})
}
This method takes event as an argument and then it will set the values state as [key]:value
pair in values object. Now to use this methods and state in other components we need to return it.
Your useForm
should look as the following,
import React, { useState } from 'react'
const useForm = () => {
//Form values
const [values, setValues] = useState({});
//Errors
const [errors, setErrors] = useState({});
//A method to handle form inputs
const handleChange = (event) => {
//To stop default events
event.persist();
let name = event.target.name;
let val = event.target.value;
//Let's set these values in state
setValues({
...values,
[name]:val,
})
}
return {
values,
errors,
handleChange,
}
}
export default useForm;
Let’s call and use this hook in the App.js
file. Open your App file and paste below code.
Let’s import useForm hook,
import useForm from "./Hooks/useForm";
Let’s deconstruct states and method from hook.
//Custom hook call
const { handleChange, values, errors } = useForm();
Make sure to attach handleChange
method with onChange
event of each inputs like the following👇
<input type="email" name="email" placeholder="E-mail" onChange={handleChange} />
<input type="password" name="password" placeholder="password" onChange={handleChange} />
<input type="text" name="username" placeholder="username" onChange={handleChange} />
You can check values state by using console.log(values)
inside the App.js file.
Implementing Form Validation in Custom Hook
Let’s create a validation function in useForm
hook.
Open useForm.js
file, and write following code,
const validate = (event, name, value) => {
//A function to validate each input values
switch (name) {
case 'username':
if (value.length <= 4) {
// we will set the error state
setErrors({
...errors,
username: 'Username atleast have 5 letters'
})
} else {
// set the error state empty or remove the error for username input,
// omit function removes/omits the value from given object and returns a new object
let newObj = omit(errors, "username");
setErrors(newObj);
}
break;
default:
break;
}
}
Let’s break down validate
function.
This function takes 3 arguments,
event
: If you want target element for customization or styling.name
: name of the element.values
: values of the element.
Here we have used switch
statement for different elements, so that you can validate different elements. The key of the switch
is the name of the element.
Now for the first case we have username
, So, in this case we have a condition that, if length of the username
value is <=
4 then we will set the error state else we will remove that error if exists.
Here we have used omit function, since we can not manipulate state object directly. This omit function takes two arguments first is the
object
and second is thename of the error
you want to remove, Now omit function remove that error if it exist then it returns the new object.
Now in the handleChange
method use validate function and make sure to pass all the arguments. Refer the following code👇
1import React, { useState } from 'react'
2import {omit} from 'lodash'
3
4const useForm = () => {
5 //Form values
6 const [values, setValues] = useState({});
7 //Errors
8 const [errors, setErrors] = useState({});
9
10 const validate = (event, name, value) => {
11 //A function to validate each input values
12 switch (name) {
13 case 'username':
14 if(value.length <= 4){
15 // we will set the error state
16
17 setErrors({
18 ...errors,
19 username:'Username atleast have 5 letters'
20 })
21 }else{
22 // set the error state empty or remove the error for username input
23 //omit function removes/omits the value from given object and returns a new object
24 let newObj = omit(errors, "username");
25 setErrors(newObj);
26 }
27 break;
28
29 case 'email':
30 if(
31 !new RegExp( /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/).test(value)
32 ){
33 setErrors({
34 ...errors,
35 email:'Enter a valid email address'
36 })
37 }else{
38 let newObj = omit(errors, "email");
39 setErrors(newObj);
40 }
41 break;
42
43 case 'password':
44 if(
45 !new RegExp(/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9]).{8,}$/).test(value)
46 ){
47 setErrors({
48 ...errors,
49 password:'Password should contains atleast 8 charaters and containing uppercase,lowercase and numbers'
50 })
51 }else{
52 let newObj = omit(errors, "password");
53 setErrors(newObj);
54 }
55 break;
56
57 default:
58 break;
59 }
60 }
61
62 //A method to handle form inputs
63 const handleChange = (event) => {
64 //To stop default events
65 event.persist();
66
67 let name = event.target.name;
68 let val = event.target.value;
69
70 validate(event,name,val);
71
72 //Let's set these values in state
73 setValues({
74 ...values,
75 [name]:val,
76 })
77
78 }
79 return {
80 values,
81 errors,
82 handleChange,
83 }
84}
85
86export default useForm
87
Line no:31 Here, for email
we are using regular expression to validate the email
value. If is is false
then if condition becomes true
and we will set the error accordingly. Same for the password. You can customize this regular function as per your requirements.
Line no:79 In this line we are calling validate
function which will validate all the inputs.
Handling The Submit Event
Let’s create handle submit function. open App.js
file and write the following code.
//Final submit function
const formLogin = () => {
console.log("Callback function when form is submitted!");
console.log("Form Values ", values);
}
Now this is the function you can customize and will be called when the form is submitted. Let’s pass this function in the useForm
hook as a callback function.
//Custom hook call
const { handleChange, values, errors, handleSubmit } = useFor(formLogin);
This is how you can pass your custom functions to the hooks. Now let’s open the useForm.js
file and deconstruct callback function from the props as the following code block👇
const useForm = (callback) => {
...
Let’s create handleSubmit
function.
const handleSubmit = (event) => {
if(event) event.preventDefault();
if(Object.keys(errors).length === 0 && Object.keys(values).length !==0 ){
callback();
}else{
alert("There is an Error!");
}
}
In this function, if length of errors is 0 and length of values is not zero (values are not empty) then it will call the callback function else it will alert the user.
You should customize these conditions as per your requirements.
Don’t forget to add handleSubmit
function in the return
statement. Now let’s open the App.js
file and deconstruct handleSubmit
method from the useForm
hook as the following,
const { handleChange, values, errors, handleSubmit } = useFor(formLogin);
Let’s connect this method with the form with the onSubmit
event.
<form onSubmit={handleSubmit}>
Displaying the errors
Now to display errors we can do something like this,
<form onSubmit={handleSubmit}>
<input type="email" name="email" placeholder="E-mail" onChange={handleChange} />
{
errors.email && <h3>{errors.email}</h3>
}
<input minLength='8' type="password" name="password" placeholder="password" onChange={handleChange} />
{
errors.password && <h3>{errors.password}</h3>
}
<input type="text" minLength='5' required name="username" placeholder="username" onChange={handleChange} />
{
errors.username && <h3>{errors.username}</h3>
}
<input type="submit" value="Submit" className="submit" />
</form>
First we will check if there is any error then we will display it. You can watch the above video for manipulating the style of the component based on it’s values.