Imagine spending 30 minutes filling out a job application, only to be told your submission failed because you entered an invalid email address. The frustration can be overwhelming, especially if the error message is vague and unhelpful. In this article, we'll explore the essential concept of form validation in React, a very important skill for ensuring a smooth user experience in web applications. We'll walk through setting up a form component, implementing validation logic, displaying error messages, and even exploring popular libraries that can simplify the process.
Prerequisites
HTML and CSS knowledge
Javascript fundamentals
Basic react knowledge (Including state management and event handling)
What is Form Validation?
Form validation is a way of ensuring that data entered in a form is accurate, complete, and aligns with specific rules and conditions.
Benefits of form validation
It ensures data integrity and accuracy.
It improves user experience by providing immediate and clear feedback on what needs to be changed.
It prevents users from submitting invalid or harmful data.
Core concepts of form validation
Required fields
Format validation (e.g Email, phone numbers)
Length Validation (Minimum and Maximum characters)
Pattern validation (passwords)
Custom validation logic (Specific rules based on the application's needs)
In this article, we're going to be considering the first four.
Different approaches to form validation in react
Using built-in HTML5 validation attributes.
Implementing custom validation logic in Javascript.
React hooks for state management and validation.
Form Validation libraries like Formik, React Hook Form, Redux Form, etc.
In this article, we'll be using react hooks.
Implementing form validation using react hooks
Before we dive into the implementation, I assume that you have already created your React app. If you haven't, I suggest doing so using Create React App for a quick and easy start. You can set up your project by running the following commands in your terminal:
npx create-react-app dynamic-form
cd dynamic-form
npm start
Once your app is up and running, we can proceed to create the form component.
Creating the form component
First, we'll set up the basic form structure using a functional component. This form should include basic inputs like username, email address, and password, which we will validate.
In your src folder, create a file called DynamicForm.js (or whatever you wish to call it), and create a component using the same name.
import React from "react";
function DynamicForm() {
return (
<div>
<h1>Dynamic Form</h1>
</div>
);
}
export default DynamicForm;
NB: Clean up the app.js file and import the DynamicForm component there to display the component.
Your app.js file should look like this:
import "./App.css";
import DynamicForm from "./DynamicForm";
function App() {
return (
<div className="App">
<DynamicForm />
</div>
);
}
export default App;
Next, we'll create our form component with the basic inputs
import React from "react";
function DynamicForm() {
return (
<div>
<h1>Dynamic Form</h1>
<form action="">
<div>
<label htmlFor="username">Username</label>
<br />
<input type="text" name="name" placeholder="name" />
</div>
<div>
<label htmlFor="email">Email</label>
<br />
<input type="text" name="email" placeholder="email" />
</div>
<div>
<label htmlFor="password">Password</label>
<br />
<input type="text" name="password" placeholder="********" />
</div>
<div>
<label htmlFor="confirmPassword">Password</label>
<br />
<input type="text" name="confirmPassword" placeholder="********" />
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default DynamicForm;
Next, we'll initialize state for our form fields using the useState Hook
import React, { useState } from "react";
function DynamicForm() {
const [errors, setErrors] = useState({});
const [formData, setFormData] = useState({
name: "",
email: "",
password: "",
confirmPassword: "",
});
const [isFormValid, setIsFormValid] = useState(false);
return (
<div>
<h1>Dynamic Form</h1>
<form action="">
<div>
<label htmlFor="username">Username</label>
<br />
<input type="text" name="name" placeholder="name" />
</div>
<div>
<label htmlFor="email">Email</label>
<br />
<input type="text" name="email" placeholder="email" />
</div>
<div>
<label htmlFor="password">Password</label>
<br />
<input type="text" name="password" placeholder="********" />
</div>
<div>
<label htmlFor="confirmPassword">Password</label>
<br />
<input type="text" name="confirmPassword" placeholder="********" />
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default DynamicForm;
Next, we'll create an event handler to handle changes made to our inputs, and add the handleChange method to our inputs.
import React, { useState } from "react";
function DynamicForm() {
const [errors, setErrors] = useState({});
const [formData, setFormData] = useState({
name: "",
email: "",
password: "",
confirmPassword: "",
});
const [isFormValid, setIsFormValid] = useState(false);
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value,
});
};
return (
<div>
<h1>Dynamic Form</h1>
<form action="">
<div>
<label htmlFor="username">Username</label>
<br />
<input
type="text"
name="name"
placeholder="name"
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="email">Email</label>
<br />
<input
type="text"
name="email"
placeholder="email"
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="password">Password</label>
<br />
<input
type="text"
name="password"
placeholder="********"
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="confirmPassword">Password</label>
<br />
<input
type="text"
name="confirmPassword"
placeholder="********"
onChange={handleChange}
/>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default DynamicForm;
Next, we'll define the function that validates the inputs. We can call that validateField. It will take two parameters: name and value.
const validateField = (name, value) => {};
Now, we'll set validation conditions for the inputs. These are the conditions we'll set:
- Username must be 3 characters and above.
- Email must be a valid email address.
- Password must be more than 8 characters and contain a number.
- Confirm Password must match the password.
We will use the switch operator to set the conditions, for a simpler format.
import React, { useState } from "react";
function DynamicForm() {
const [errors, setErrors] = useState({});
const [formData, setFormData] = useState({
name: "",
email: "",
password: "",
confirmPassword: "",
});
const [isFormValid, setIsFormValid] = useState(false);
const validateField = (name, value) => {
const validationErrors = { ...errors };
switch (name) {
case "name":
if (value.length < 3) {
validationErrors.name = "Name must be more than 3 characters.";
} else {
delete validationErrors.name;
}
break;
case "email":
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
validationErrors.email = "Email is not valid";
} else {
delete validationErrors.email;
}
break;
case "password":
if (value.length < 8) {
validationErrors.password = "Password must be more than 8 characters";
} else if (!/\d/.test(value)) {
validationErrors.password = "Password must contain a number";
} else {
delete validationErrors.password;
}
break;
case "confirmPassword":
if (value != formData.password) {
validationErrors.confirmPassword = "Passwords must match";
} else {
delete validationErrors.password;
}
break;
default:
break;
}
setErrors(validationErrors)
};
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value,
});
};
return (
<div>
<h1>Dynamic Form</h1>
<form action="">
<div>
<label htmlFor="username">Username</label>
<br />
<input
type="text"
name="name"
placeholder="name"
onChange={handleChange}
value={formData.name}
/>
</div>
<div>
<label htmlFor="email">Email</label>
<br />
<input
type="text"
name="email"
placeholder="email"
onChange={handleChange}
value={formData.email}
/>
</div>
<div>
<label htmlFor="password">Password</label>
<br />
<input
type="password"
name="password"
placeholder="********"
onChange={handleChange}
value={formData.password}
/>
</div>
<div>
<label htmlFor="confirmPassword">Password</label>
<br />
<input
type="password"
name="confirmPassword"
placeholder="********"
onChange={handleChange}
value={formData.confirmPassword}
/>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default DynamicForm;
Now, we've set the conditions, let's add it to the form to make sure the inputs are checked before the form can be submitted.
First, we'll create a handleSubmit function to add to the submit button.
const handleSubmit = (e) => {
e.preventDefault();
};
Next, we'll use the useEffect hook to manage the validation logic and update the form's validity status when there are changes in the state
useEffect(() => {
setIsFormValid(
Object.keys(errors).length === 0 &&
formData.name.length >= 3 &&
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email) &&
formData.password.length >= 8 &&
/\d/.test(formData.password) &&
formData.confirmPassword === formData.password
);
}, [errors, formData]);
Don't forget to import useEffect at the top of your file.
Next, add this line to the handleChange function:
validateField(name, value);
Next, in the input section, we'll add the error messages below the input.
import React, { useState, useEffect } from "react";
function DynamicForm() {
const [errors, setErrors] = useState({});
const [formData, setFormData] = useState({
name: "",
email: "",
password: "",
confirmPassword: "",
});
const [isFormValid, setIsFormValid] = useState(false);
const validateField = (name, value) => {
const validationErrors = { ...errors };
switch (name) {
case "name":
if (value.length < 3) {
validationErrors.name = "Name must be more than 3 characters.";
} else {
delete validationErrors.name;
}
break;
case "email":
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
validationErrors.email = "Email is not valid";
} else {
delete validationErrors.email;
}
break;
case "password":
if (value.length < 8) {
validationErrors.password = "Password must be more than 8 characters";
} else if (!/\d/.test(value)) {
validationErrors.password = "Password must contain a number";
} else {
delete validationErrors.password;
}
break;
case "confirmPassword":
if (value != formData.password) {
validationErrors.confirmPassword = "Passwords must match";
} else {
delete validationErrors.password;
}
break;
default:
break;
}
setErrors(validationErrors);
};
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value,
});
validateField(name, value);
};
useEffect(() => {
setIsFormValid(
Object.keys(errors).length === 0 &&
formData.name.length >= 3 &&
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email) &&
formData.password.length >= 8 &&
/\d/.test(formData.password) &&
formData.confirmPassword === formData.password
);
}, [errors, formData]);
const handleSubmit = (e) => {
e.preventDefault();
if (isFormValid) {
const dataString = `
Name: ${formData.name}
Email: ${formData.email}
Password: ${formData.password}
Confirm Password: ${formData.confirmPassword}
`;
alert(`Form Submitted Successfully!\n${dataString}`);
}
};
return (
<div>
<h1>Dynamic Form</h1>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="username">Username</label>
<br />
<input
type="text"
name="name"
placeholder="name"
onChange={handleChange}
value={formData.name}
/>
{errors.name && <p style={{ color: "red" }}>{errors.name}</p>}
</div>
<div>
<label htmlFor="email">Email</label>
<br />
<input
type="text"
name="email"
placeholder="email"
onChange={handleChange}
value={formData.email}
/>
{errors.name && <p style={{ color: "red" }}>{errors.email}</p>}
</div>
<div>
<label htmlFor="password">Password</label>
<br />
<input
type="password"
name="password"
placeholder="********"
onChange={handleChange}
value={formData.password}
/>
{errors.name && <p style={{ color: "red" }}>{errors.password}</p>}
</div>
<div>
<label htmlFor="confirmPassword">Password</label>
<br />
<input
type="password"
name="confirmPassword"
placeholder="********"
onChange={handleChange}
value={formData.confirmPassword}
/>
{errors.name && (
<p style={{ color: "red" }}>{errors.confirmPassword}</p>
)}
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default DynamicForm;
At this point, you should see your error messages as you type in your inputs.
Next, we want to get an alert with the inputs if the form was submitted successfully.
Add this line of code to the handleSubmit function
if (isFormValid) {
const dataString = `
Name: ${formData.name}
Email: ${formData.email}
Password: ${formData.password}
Confirm Password: ${formData.confirmPassword}
`;
alert(`Form Submitted Successfully!\n${dataString}`);
}
Finally, we want to disable the submit button until all fields are valid.
<button type="submit" disabled={!isFormValid}>
Submit
</button>
And everything should work as expected now! You should get your error messages as you type, and they should clear when you meet the conditions.
Libraries that can simplify the process
If you don't want to go through that whole process, here are some form validation libraries you can explore some that can handle it.
Key Takeaways
- Importance of Form Validation: Proper validation not only prevents invalid data from being submitted but also guides users in filling out forms correctly, reducing frustration and improving overall satisfaction.
- State Management: Utilizing React's useState hook allows us to manage form data and validation errors effectively, providing a seamless user experience.
- Real-Time Feedback: Implementing real-time validation enhances user interaction by providing immediate feedback, making forms more intuitive.
- Using Libraries: For more complex forms, consider leveraging libraries like Formik or React Hook Form to simplify validation and state management.
Final Thoughts
Form validation is a crucial aspect of web development that every React developer should master. By implementing effective validation strategies, you can create forms that not only function well but also provide a positive experience for users.
I encourage you to experiment with the concepts discussed in this article and explore additional validation techniques and libraries to further enhance your forms.
If you have any questions or would like to share your experiences with form validation in React, feel free to leave a comment below. Happy coding!
Top comments (0)