Once of the common tasks as a react developer you will encounter is, build forms
That means that if you wanna do it the proper way you have to validate the fields, handle the state of the fields , errors, types etc, things will get complex
And as you introduce more complexity to your forms the more difficult develop will be, This is where React-Hook-Form kicks in
Tools we are going to use :
- Node (version 16 or greater)
- Yarn
Set Up:
We are going to be using create react app for this project, but whichever frontend react tooling you like is good to go
initialize your project with :
yarn create react-app rhf-tut
then:
cd rhf-tut
Install dependencies:
Chakra Ui:
yarn add @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^6
React Hook form + Yup:
yarn add react-hook-form @hookform/resolvers yup
Usage of the React hook form:
Basically we import the useForm hook from react-hook-form
, this hook will return us, some functions like
register
:this 'register' each of the inputs will use , it basically returns props needed to control the input)handleSubmit
: This function is going to be passed to the onSubmit of the form, and it will have as parameter a function that will get the data of our formformState
: It will held the state of our form , values like the errors, if the form was successFully submitted , or if the form is dirty
There is some other functions that we will be using later, but basically these are the most commonly used
import { useForm } from "react-hook-form";
const UserForm = () => {
const {register, handleSubmit, formState: { errors }} = useForm();
return (
// Inputs code here
);
};
export default UserForm;
Text Inputs:
As i told before, register function is meant to 'register' our inputs, so let's import Input from chakra-ui
and pass down on props {...register('textInput')}
using as argument of register the name for our input :
import { Input } from '@chakra-ui/react';
import { useForm } from "react-hook-form";
const UserForm = () => {
const {register, handleSubmit, formState: { errors }} = useForm();
return (
<Input {...register('textInput')} />
);
};
export default UserForm;
Radio Inputs:
For radio inputs , we are going to do something a little bit different, since the RadioGroup component of chakra-ui (which is the one that handles the onChange and value ) is not an input so , if we use the register function won't work , we'll have to use the <Controller />
component from react-hook-form that gives us a little bit more control over our inputs
import { Radio, RadioGroup, Stack } from '@chakra-ui/react';
import { useForm, Controller } from "react-hook-form";
const UserForm = () => {
const {register, handleSubmit, formState: { errors }, control} = useForm();
return (
<Controller
name="radio"
control={control}
render={({ field: { onChange, value } }) => (
<RadioGroup onChange={onChange} value={value}>
<Stack direction="row">
<Radio value="1">First</Radio>
<Radio value="2">Second</Radio>
<Radio value="3">Third</Radio>
</Stack>
</RadioGroup>
)}
/>
);
};
export default UserForm;
Checkbox Inputs:
For checkbox we can use the register function as we do with normal text input but this time adding the value attribute , as you can notice this time we use the register function passing the same name.
import { Radio, RadioGroup, Stack } from '@chakra-ui/react';
import { useForm, Controller } from "react-hook-form";
const UserForm = () => {
const {register, handleSubmit, formState: { errors }, control} = useForm();
return (
<FormControl>
<FormLabel>Choose many</FormLabel>
<Stack direction="row">
<Checkbox value="1" {...register('multiple')} >
Here
</Checkbox>
<Checkbox value="2" {...register('multiple')} >
Here
</Checkbox>
</Stack>
</FormControl>
);
};
export default UserForm;
Handling submit
Now comes the time we need to get the data from our form to send it to our backend or any other action we would like to do with the data of our form,
const UserForm = () => {
const {register, handleSubmit, formState: { errors }} = useForm();
const onSubmit = (formData) => apiCallToSaveData(formData);
const onInvalid = () => alert('This form is invalid try again');
return (
<VStack
as="form"
minWidth="30%"
bgColor="#FFF"
padding="2em"
borderRadius="12px"
shadow="md"
mt="4em"
onSubmit={handleSubmit(onSubmit, onInvalid)}
>
// here some inputs
</VStack>
);
};
handleSubmit
will take our onSubmit function and optionally onInvalid function as parameters , it will pass to onSubmit all the data in our form , and onInvalid , will be executed just in case our form is invalid, that's our next topic
Validation
To verify the data in our form with react hook form we are going to use Yup, an awesome tool for schema validation , this is my favorite approach, and one of the things i love the most of React hook form, because you just have to describe the way your data looks, with its types, if it is required or not, length, etc
We define our schema of types:
import * as yup from "yup";
const formSchema = yup
.object({
name: yup.string(),
email: yup
.string()
.email("Please introduce a valid email")
.required("Email is required"),
phoneNumber: yup
.string()
.matches(phoneRegExp, "It doesn't seem to be a phone number")
.length(11, "Phone number is too short"),
multiple: yup
.array()
.of(yup.string())
.ensure()
.compact()
.min(1, "I bet you like one of those").required(),
radio: yup.string(),
})
.required();
as you can see we pass a string as parameter on some functions inside the schema, like in required or email, that's the text we are going to display in case that The data in our form doesn't match that property of our schema,
And add it to RHF :
import { yupResolver } from "@hookform/resolvers/yup";
...
const {
register,
handleSubmit,
formState: { errors },
control,
} = useForm({
resolver: yupResolver(formSchema),
});
Display errors:
As you can notice above we are getting errors
from formState
, this will have the errors found in the form, we can access to them using the same name we assigned to the input, we are going to add a proper error info to our form like this:
....
<FormControl isInvalid={errors?.email}>
<FormLabel htmlFor="email">Email address</FormLabel>
<Input type="text" {...register("email")} />
{errors?.email ? (
<FormErrorMessage>
{errors.email.message}
</FormErrorMessage>
) : (
<FormHelperText>We'll never share your email.</FormHelperText>
)}
</FormControl>
.....
the message will be the one defined on our schema (For me that's so awesome, i love it), and if you need the type of the error, just error.email.type
(it will depend on the schema of yup), in case you need to execute a different action than show the error message while error
Thanks for reading,i hope it's useful for you, you can leave any suggestion or doubt about it in the comments box below 👇, also you can find the Code used for this Post here
Top comments (0)