How to use React hook form with your custom form components!
How to get this working with a simple validation schema and a custom input component!
- A newer way to handle form submission and form validation-
- A little easier to use than Formik
Install it into your project using npm:
npm install react-hook-form
Set up needed in the main form
In our Form component, you can declare the useForm hook from react-hook-form, and import all the properties we need.
The register method helps you register an input field into the React Hook Form so that it is available for the validation and its value can be tracked for changes. We pass this as a prop, to all our input fields.
The reset method allows us to reset all our form values after we submit or if we click a reset button. We just call it whenever we need it. Optional!
The handleSubmit method, is called as soon as the user submits the form and it is valid. It will allow us to call our own submit function and pass the values to it.
The formState / errors allows us to grab our errors and messages. We will use the errors array to display errors under form fields or at the top of page.
import "./styles.css";
import { useForm } from "react-hook-form";
import Input from "../input-field";
export const NewTodoForm = (props) => {const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm();const onSubmit = (data) => {
console.log (data);
reset();
};return (
<form onSubmit={handleSubmit(onSubmit)}>
<h1 className="form-title">Create a new Todo</h1> <Input
type="text"
name="title"
label="Todo (e.g do laundry)"
errors={errors}
register={register}
validationSchema={{
required: "Todo text is required",
minLength: {
value: 3,
message: "Please enter a minimum of 3 characters"
}
}}
required
/> <Input
type="date"
name="date"
label="Due Date"
errors={errors}
register={register}
validationSchema={{
required: "Todo deadline is required"
}}
required
/> <input type="submit" />
</form>
);
};
It is important to note, that for validation we pass a validationSchema prop to our custom input component which just follows the syntax
validationSchema={{
required: "Todo text is required",
minLength: {
value: 3,
message: "Please enter a minimum of 3 characters"
}
}}
So we could also add other things in our validationSchema like:
- required: Indicates if the field is required or not. If this property is set to true then the field cannot be empty. (already shown in the above example ✅)
- minlength and maxlength: sets the minimum and maximum length for a string input value.
- type: Indicates the type of the input field; it can be email, number, text, or any other standard HTML input types.
- pattern: Defines a pattern for the input value using a regular expression.
Set up needed in the Custom Input component
- Whenever we make forms, we usually create a reusable input component
- Since all input components will have a label, input field, error messages, validation, etc..
- We can define this all once and re-use it for all our input fields
So we pass all these as props..
Regarding react-hook-form, we pass the register function, and we call it inside the HTML input itself.
{…register(name, validationSchema)}
As you can see below we pass it an object of validationSchema object obtained from the main form as a prop. We also pass it the name of the input field to register it properly to the react-hook-form. This way it can validate it correctly.
We also pass the errors array from the react-hook-form which has a list of all the errors, for the entire form. These errors names all correspond to the validationSchema we defined and passed earlier from the form itself. They will fall under the name of input field, and the type of error (required, minLength, maxLength, pattern ..etc)
import "./styles.css";const Input = ({ name, label, register, errors, required, type, validationSchema }) => ( <div className="form-control-input">
<label htmlFor={name}>
{label}
{required && "*"}
</label>
<input
id={name}
name={name}
type={type}
{...register(name, validationSchema)}
/>
{errors && errors[name]?.type === "required" && (
<span className="error">{errors[name]?.message}</span>
)}
{errors && errors[name]?.type === "minLength" && (
<span className="error">{errors[name]?.message}</span>
)}
</div>);export default Input;
That’s it!
If your form was validated correctly, you can submit your data to an API or something alternative to save it, as the values will be passed to your handleSubmit function
const onSubmit = (data) => {
console.log (data);
reset();
};
return (
<form onSubmit={handleSubmit(onSubmit)}> ...