Hello friends,
Video tutorial for this blog post you can find here: https://youtu.be/M7XYsrWorNo
Starting point for this tutorial: https://github.com/Jerga99/next-youtube-course/tree/v1.1
Complete code: https://github.com/Jerga99/next-youtube-course/commit/e31b92302b2878124dee09db32ec26d7535e611a
To validate a form we need some basic form template as an example. Here it is:
const PortfolioForm = () => {
return (
<form>
<div className="form-group">
<label htmlFor="title">Title</label>
<input
name="title"
type="text"
className="form-control"
id="title"/>
</div>
<div className="form-group">
<label htmlFor="city">Company</label>
<input
name="company"
type="text"
className="form-control"
id="company"/>
</div>
<div className="form-group">
<label htmlFor="city">Company Website</label>
<input
name="companyWebsite"
type="text"
className="form-control"
id="companyWebsite"/>
</div>
<div className="form-group">
<label htmlFor="street">Location</label>
<input
name="location"
type="text"
className="form-control"
id="location"/>
</div>
<div className="form-group">
<label htmlFor="street">Job Title</label>
<input
name="jobTitle"
type="text"
className="form-control"
id="jobTitle"/>
</div>
<div className="form-group">
<label htmlFor="description">Description</label>
<textarea
name="description"
rows="5"
type="text"
className="form-control"
id="description">
</textarea>
</div>
<button
type="submit"
className="btn btn-primary">Create
</button>
</form>
)
}
export default PortfolioForm;PortfolioForm.js
Important part to note here is that every input element should have a "name" attribute. Thanks to the "name" data can be correctly assigned to the form object as you will see later.
Let's move on,
Now we need to install package for form validation called "react hook form"
In your terminals run: npm install --save react-hook-form
Now follow the comments in the code.
// import package on top of the file
import { useForm } from "react-hook-form";
// also Alert component from bootstrap
import { Alert } from 'react-bootstrap';
const PortfolioForm = () => {
// useForm()
// 1. register -> register input
// 2. handleSubmit -> extract data from the form
// 3. errors -> object containing errors
const { register, handleSubmit, errors } = useForm();
// function to output form data
// we need to pass it to onSubmit of form element
const onSubmit = formData => {
alert(JSON.stringify(formData))
}
return (
// pass onSubmit to handleSubmit of hook form
// when button will be pressed you should see form data
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form-group">
<label htmlFor="title">Title</label>
<input
// Register input
ref={register}
name="title"
type="text"
className="form-control"
id="title"/>
</div>
<div className="form-group">
<label htmlFor="description">Description</label>
<textarea
// Register input
ref={register}
name="description"
rows="5"
type="text"
className="form-control"
id="description">
</textarea>
</div>
<button
type="submit"
className="btn btn-primary">Create
</button>
</form>
)PortfolioForm.js
Please apply register
to every input ref
of your form. Very important is to also provide onSubmit
event to the form. To onSubmit
you will pass handleSubmit
with callback function to get a data from the form.
So far so good, now we are able to get a data from the form. To validate inputs we need to provide built-in validators to register
function.
Available build in validators are:
- required
- min
- max
- minLength
- maxLength
- pattern
- validate
You can also create your own validator but about this later.
Now let's apply validators to title and city input
<div className="form-group">
<label htmlFor="title">Title</label>
<input
// required, maxLength validators
// you need to provide values to the inputs and max lenght of characters is 30
ref={register({required: true, maxLength: 30 })}
name="title"
type="text"
className="form-control"
id="title"/>
// Check for "title" errors
{ errors.title &&
// if errors then display alert
<Alert variant="danger">
// if title error type is "required" display following message
{ errors.title?.type === "required" && <p>Title is required</p> }
// if title error type is "maxLength" display following message
{ errors.title?.type === "maxLength" && <p>Max length of title is 30 characters!</p> }
</Alert>
}
<div className="form-group">
<label htmlFor="city">Company</label>
<input
ref={register({required: true, minLength: 5})}
name="company"
type="text"
className="form-control"
id="company"/>
{ errors.company &&
<Alert variant="danger">
{ errors.company?.type === "required" && <p>Company is required</p> }
{ errors.company?.type === "minLength" && <p>Min length of title is 5 characters!</p> }
</Alert>
}
</div>
<div className="form-group">
<label htmlFor="city">Company Website</label>
<input
ref={register({required: true, pattern: /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/})}
name="companyWebsite"
type="text"
className="form-control"
id="companyWebsite"/>
{ errors.companyWebsite &&
<Alert variant="danger">
{ errors.companyWebsite?.type === "required" && <p>Website is required</p> }
{ errors.companyWebsite?.type === "pattern" && <p>Enter a valid url!</p> }
</Alert>
}
</div>
</div>PortfolioForm.js
Important part here is to register
built-in validator. To display error message you need to check for a specific error located in the error object under the key of input "name", e.g: errors.title
on the input of title
To check for a specific error you need to check for an error type, errors.title?.type==="required"
whereas type is the key under which you registered validator on the input: ref={register({required: true, maxLength: 30})}
Now let's talk about custom validators. It's actually very simple to create one.
Validator is just a function that takes a value from the input and return true or false. When false is returned error is set on the errors
object. When true is returned error is removed, which ultimately means that input is valid.
Let's create a simple validator which verifies input value is starting with an uppercased letter.
// true -> input is valid
// false -> input is NOT valid
const firstLetterUpper = iText => {
// if there is no value in input return true, input is VALID
if (!iText) { return true; }
// if first letter is uppercased return true, input is VALID
if (iText[0].toUpperCase() === iText[0]) {
return true;
}
// in any other case return false, input is INVALID
return false;
// Or just simply return this
// return iText[0].toUpperCase() === iText[0];
}PortfolioForm.js
Register the validator on input.
ref={register({required: true, maxLength: 30, validate: { firstLetterUpper }})}PortfolioForm.js
Now we are able to check for this error type:
{ errors.title?.type === "firstLetterUpper" && <p>First letter should be uppercased!</p> }PortfolioForm.js
Already! This is how you can implement a simple validation with react hook form. In the second part of form validation you are going to learn how to validate more complicated components as for example "date pickers".
If you would like to see more of my courses please visit: https://eincode.com
Best,
Filip