For almost every form that you create, you will want some sort of validation. In React, working with and validating forms can be a bit verbose, so in this article we are going to use a package called Formik to help us out!
Here’s a sneak peak at what we are going to create.
For this demo, I’ll be using CodeSandbox. You can use CodeSandbox as well or use your local environment. Totally up to you.
Regardless of what you use for this demo, you need to start with a new React app using Create React App. In CodeSandbox, I’m going to choose to do just that.
Now that we have our initial project created, we need to install three packages.
In your terminal, you’ll need to install Formik.
npm install Formik
I’ll do the same in the CodeSandbox dependency GUI.
Now install email-validator.
npm install email-validator
Again installing from the CodeSandbox GUI.
npm install Yup
And again in CodeSandbox.
Now, we can start to stub out our ValidatedFormComponent. For now, we just want to create the basics and import it into the root file in the app to see it get displayed.
So, create a new file in your src directory called ValidatedLoginForm.**js. **Inside of that file, add the basic code for a functional component.
import React from "react";
const ValidatedLoginForm = () => (
# Validated Form Component
);
export default ValidatedLoginForm;
Then, include it in your index.js file.
function App() {
return (
);
}
and you should see it displayed.
Now, let’s start with the Formik stuff. First, import Formik, Email-Valiator, and Yup in your new component.
import { Formik } from "formik";
import _ as EmailValidator from "email-validator";
import _ as Yup from "yup";
Now, let’s stub out the Formik tag with initial values. Think of initial values as setting your state initially.
You’ll also need an onSubmit callback. This callback will take two parameters, *_values *_and an object that we can destructure. The values represented the input values from your form. I’m adding some dummy code here to simulate an async login call, then logging out what the values are.
In the callback, I’m also calling the setSubmitting function that was destructured from the second parameters. This will allow us to enable/disable the submit button while the asynchronous login call is happening.
<Formik
initialValues={{ email: "", password: "" }}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
console.log("Logging in", values);
setSubmitting(false);
}, 500);
}}
>
# Validated Login Form
The Formik component uses render props to supply certain variables and functions to the form that we create. If you’re not very familiar with render props, I would take a second to check out Render Props Explained.
In short, render props are used to pass properties to children elements of a component. In this case, Formik will pass properties to our form code, which is the child. Notice that I’m using destructuring to get a reference to several specific variables and functions.
{ props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange,
handleBlur,
handleSubmit
} = props;
return (
# Validated Login Form
);
}}
Now, we can actually start to write the code to display the form. For what it’s worth, in the finished CodeSandbox, I also created a LoginForm.*_js *_component to show how basic login forms are handled from scratch. You can also use that as a reference for the form we are going to add now.
The form is pretty simple with two inputs (email and password), labels for each, and a submit button.
{ props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange,
handleBlur,
handleSubmit
} = props;
return (
Email
Password
<input
name="password"
type="password"
placeholder="Enter your password"
/>
Login
);
}}
Notice that the *_onSubmit *_is calling the *_handleSubmit *_from the props.
I mentioned earleir that we could disable our submit button while the user is already attempting to login. We can add that small change now by using the *_isSubmitting *_property that we destructured from props above.
Login
I would recommend adding the CSS from the finished CodeSandbox as well. Otherwise you won’t get the full effect. You can copy the below css into your styles.css file.
.App {
font-family: sans-serif;
}
h1 {
text-align: center;
}
form {
max-width: 500px;
width: 100%;
margin: 0 auto;
}
label,
input {
display: block;
width: 100%;
}
label {
margin-bottom: 5px;
height: 22px;
}
input {
margin-bottom: 20px;
padding: 10px;
border-radius: 3px;
border: 1px solid #777;
}
input.error {
border-color: red;
}
.input-feedback {
color: rgb(235, 54, 54);
margin-top: -15px;
font-size: 14px;
margin-bottom: 20px;
}
button {
padding: 10px 15px;
background-color: rgb(70, 153, 179);
color: white;
border: 1px solid rgb(70, 153, 179);
background-color: 250ms;
}
button:hover {
cursor: pointer;
background-color: white;
color: rgb(70, 153, 179);
}
Now we need to figure out how to validate our inputs. The first question is, what constraints do we want to have on our input. Let’s start with email. Email input should…
Password input should…
We’ll cover two ways to create these messages, one using Yup and one doing it yourself. We recommend using Yup and you’ll see why shortly.
The first option is creating our validate function. The purpose of the function is to iterate through the values of our form, validate these values in whatever way we see fit, and return an **errors _object that has key value pairs of _value->message.
Inside of the Formik tag, you can add the following code. This will always add an “Invalid email” error for email. We will start with this and go from there.
validate={values => {
let errors = {};
errors.email = "Invalid email";
return errors;
}}
Now, we can ensure that the user has input something for the email.
validate={values => {
let errors = {};
if (!values.email) {
errors.email = "Required";
}
return errors;
}}
Then, we can check that the email is actually a valid looking email by using the email-validator package. This will look almost the same as the equivalent check for email.
validate={values => {
let errors = {};
if (!values.email) {
errors.email = "Required";
} else if (!EmailValidator.validate(values.email)) {
errors.email = "Invalid email address";
}
return errors;
}}
That takes care of email, so now for password. We can first check that the user input something.
validate={values => {
let errors = {};
if (!values.password) {
errors.password = "Required";
}
return errors;
}}
Now we need to check the length to be at least 8 characters.
validate={values => {
const passwordRegex = /(?=.*[0-9])/;
if (!values.password) {
errors.password = "Required";
} else if (values.password.length < 8) {
errors.password = "Password must be 8 characters long.";
}
return errors;
}}
And lastly, that the password contains at least one number. For this, we can use regex.
validate={values => {
let errors = {};
const passwordRegex = /(?=.*[0-9])/;
if (!values.password) {
errors.password = "Required";
} else if (values.password.length < 8) {
errors.password = "Password must be 8 characters long.";
} else if (!passwordRegex.test(values.password)) {
errors.password = "Invalida password. Must contain one number";
}
return errors;
}}
Here’s the whole thing.
validate={values => {
let errors = {};
if (!values.email) {
errors.email = "Required";
} else if (!EmailValidator.validate(values.email)) {
errors.email = "Invalid email address";
}
const passwordRegex = /(?=.*[0-9])/;
if (!values.password) {
errors.password = "Required";
} else if (values.password.length < 8) {
errors.password = "Password must be 8 characters long.";
} else if (!passwordRegex.test(values.password)) {
errors.password = "Invalida password. Must contain one number";
}
return errors;
}}
Ok, you might have noticed that handling the validate logic on our own gets a bit verbose. We have to manually do all of the checks ourselves. It wasn’t that bad I guess, but with the Yup package, it gets all the more easy!
Yup is the recommended way to handle validation messages.
Yup makes input validation a breeze!
When using Yup, we no longer will see the **Validate _property, but insead use _validationSchema. Let’s start with email. Here is the equivalent validation using Yup.
validationSchema={Yup.object().shape({
email: Yup.string()
.email()
.required("Required")
})}
Much shorter right?! Now, for password.
validationSchema={Yup.object().shape({
email: Yup.string()
.email()
.required("Required"),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/(?=.*[0-9])/, "Password must contain a number.")
})}
Pretty SWEET!
Now that we have the logic for creating error messages, we need to display them. We will need to update the inputs in our form a bit.
We need to update several properties for both email and password inputs.
Let’s start by updating value, onChange, and onBlur. Each of these will use properties from the render props.
<input
name="email"
type="text"
placeholder="Enter your email"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
/>
Then we can add a conditional “error” class if there are any errors. We can check for errors by looking at the errors object (remeber how we calculated that object ourselves way back when).
We can also check the touched property, to see whether or not the user has interacted with the email input before showing an error message.
<input
name="email"
type="text"
placeholder="Enter your email"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
className={errors.email && touched.email && "error"}
/>
And lastly, if there are errors, we will display them to the user. All in all, email will look like this.
Email
<input
name="email"
type="text"
placeholder="Enter your email"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
className={errors.email && touched.email && "error"}
/>
{errors.email && touched.email && (
{errors.email}
)}
Now we need to do the same with password. I won’t walk through each step beause they are exactly the same as email. Here’s the final code.
Password
<input
name="password"
type="password"
placeholder="Enter your password"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
className={errors.password && touched.password && "error"}
/>
{errors.password && touched.password && (
{errors.password}
)}
Let’s try it out! You can start by clicking the button without entering anything. You should see validation messages.
Now, we can get more specific for testing messages. Refresh your page to do this.Click inside of the email input, but don’t type anything.
Then, click away from the input. You should see the “Required” message pop up. Notice that this message doesn’t pop up automatically when the page loads. We only want to display error messages after the user has interacted with the input.
Now, start to type. You should get a message about not being a valid email.
And lastly, type in a valid looking email, and your error message goes away.
Now, same for password. Click on the input, then away, and you’ll get the required message.
Then, start typing and you’ll see the length validation.
Then, type 8 or more characters that does not include a number, and you’ll see the “must contain a number” message.
And lastly, add a number, and error messages go away.
Whew, that was a long one! Again, validation can be a tricky thing, but with the help of a few packages, it becomes a bit easier. At the end of the day though, I think we’ve got a pretty legit login form!
✅ 30s ad
☞ Master ReactJS: Learn React JS from Scratch
☞ Learn ReactJS: Code Like A Facebook Developer
☞ ReactJS Course: Learn JavaScript Library Used by Facebook&IG
☞ React: Learn ReactJS Fundamentals for Front-End Developers
#reactjs #javascript