Form validation with useForm hook
Tags: react, javascript, webdev, beginners
Published: 2022-03-12 09:41:56


Form validation is a crucial and a must for every website that deals with user's data, But the form validation process can sometimes be a little complicated and messy, that's why solutions such as *yup* or *Formik* exist. Today we're gonna take a look at one of the react's popular hooks: *useForm* and see how we can validate user's input and display error messages should it be rendered. 

## Why React Hook Form ?

- Built with performance, UX and DX in mind
- Embraces native HTML form validation
- Out of the box integration with UI libraries
- Small size and no dependencies
- Support Yup, Zod, Superstruct, Joi, Vest, class-validator, io-ts, nope and custom

## Let's Get Started

I already made a simple registration form to apply on, Go ahead and clone the repo: [Click me](https://github.com/m0nm/react-useform-example/tree/main)


![react form](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5hpaedwwxj79zw01a8h4.png)

`git clone https://github.com/m0nm/react-useform-example`

Install the deps
`npm install // or yarn add`

After the installation has been finished now we're ready to use React Hook Form. But first we need to consider what are we going to validate, Since this is a registration form there are a couple of points to be mindfull of: 

- The email should be a valid email
- Password must be at least 8 characters long
- 'Confirm Password' must match the password

After setting the requirements for a successful registration it's time to use the hook! 

Don't forget to Install the package!
`npm i react-hook-form // or yarn add react-hook-form`

## How does it  work ?

The `useForm` hook gives us helpful methods to simplify the way we handle forms, You can look up for all the methods on the [API reference](https://react-hook-form.com/api/useform) but the ones we need are: `register`, `handleSubmit`,  `getValues` and `formstate`

### register: 
this method allows you to register an input field and assert the validations conditions for the input

It looks like this:
```jsx 
const { register} = useForm();

<form>
<Input {...register(name, options)} />
</form>
```

- name

Where `name` is the name to reference the input for the submit data: 
```jsx
register("email") 
```
would output: `{email: valueOfTheReferencedInput}`

 - options

the way you condition the validation is by passing `options` props: 
```jsx
register("email", {
	required: true, 
	onChange: (e) => console.log("you're typing email")
})
```

### handleSubmit 
this function will submit the data **only** if the form validation **is successful**

```jsx
const { register, handleSubmit } = useForm();

<form onSubmit={handleSubmit(() => console.log("form validation is successful"))}>
  ...
</form>
```

### getValues 
This is a helper function that allow us to get the value of our inputs, so there is no need to useState them.

```jsx
const { register, getValues } = useForm();

const emailInput = getValues("email")
console.log("emailInput") // hello world 

<form> 
<input value="hello world"{...register("email")} />
</form>
```

### formState
formState is an object that provide information about our form state, One particular property we're interested in is the `errors` object which contains the error messages

```jsx
const {register, formState: {errors}} = useForm()

<form>
  <p>
	{errors.email?.message}
  </p>
  <input {...register("email")}/>
</form>
```

## Let's Apply

First we need to register our inputs with the `register` method, so go ahead and do that:

Note that we don't need to have `name` attribute on our inputs since it handled by `register` method.

```jsx
import {useForm} from "react-hook-form"

function App() {
 const {register} = useForm();
	
 return (
	<form>
			
	  {/* email */}
	  <input {...register("email")} type="email" id="email" />
			
	  {/* password */}
      <input {...register("password")} type="password" id="password" />
		
	  {/* confirm password */}
      <input {...register("confirmPassword")} type="password" id="confirmPassword" />	
		
		</form>
	)
}
```

Now we need to give each one of them the validation's conditions

- **For Email:**  
It is required and It must be a valid email, so we can use `required` and `pattern`

> The `pattern` value is called a regex expression

```jsx

 {/* email */}
 <input {...register("email"), {
		required: "Please Enter Your Email!",
		pattern: {
			value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, 
			message: "Please Enter A Valid Email!"
		}
	}} type="email" id="email" />
```

If the user did not type his email, the `errors` object from the `formState` will contain the message we typed with the `required` property, And so it's the case if the email is not valid it will contain the message from `pattern`

Now we can show the error message 
```jsx
<p className="error-message">{errors.email?.message}</p>
<input ... />
```

- **For Password:**
It is required and it must be longer than 8 characters 

```jsx
<input {...register("password"), {
		required: "Please Enter Your Password",
		minLength: {
		value: 8,
		message: "Password must be at least 8 characters long!"
		}
	}} type="password" id="password" />
```

The error message
```jsx
<p className="error-message">{errors.password?.message}</p>
<input ... />
```

-**For Confirm Password:**
This one is special because it only need to match password for validation to pass, so we can use `validate` option's method, We also need `getValue` to get the password value 

```jsx
<input {...register("confirmPassword", {
		validate: (match) => {
			const password = getValues("password")
			return match === password || "Passwords should match!"
		}
	})} type="password" id="confirmPassword" />
```

And the error message
```jsx
<p className="error-message">{errors.confirmPassword?.message}</p>
<input ... />
```

Now we're finished with the validation! It's time to use `handleSubmit`

```jsx
<form onSubmit={handleSubmit(() => console.log("user registered successfully!"))}>
	...
</form>
```

Here is our form if we submit without filling the data



![react form errors](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dbrdlfomv6d5ap7990rx.png)

And if we don't meet the conditions



![react form errors](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3t84b1zdap3m0ya6vdg5.png)


You can find the final code on this repo: [Click me](https://github.com/m0nm/react-useform-example/tree/final-app)

## Integrating with a schema validator

As an alternative for `register` options, You can use a schema validator instead such as `yup` which i'll use to demonstrate.

- Install the dependency 
`npm i yup // or yarn add yup`
- We also need the yup resolver for react-hook-form
`npm i @hookform/resolvers/yup`

Let's define our schema 
```jsx
import * as yup from "yup";

const schema = yup.object().shape({
  email: yup
    .string()
    .email("Please enter a valid email!")
    .required("Email is required!"),
  password: yup
    .string()
    .min(6, "Password must be longer than 6 characters!")
    .required("Password is required!"),
  confirmPassword: yup
    .string()
    .oneOf([yup.ref("password"), null], "Passwords must match!"),
});


function App() {
	...
}
```

Lets use the schema 

```jsx
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";

const schema = yup...

function App() {
	 const {
    register,
    getValues,
    formState: { errors },
    handleSubmit,
  } = useForm({ resolver: yupResolver(schema) });
	
	return (
		<form>
			<p className="error-message">{errors.email?.message}</p>
			<input {...register("email")} />
				...
		</form>	
	)
}
```

And that it!

## Conclusion 
We reached the end of this post, Thank you for reading i hope it was helpful and clear :). Happy react-hook-forming!!