Hi Devs...
Are you following the basic method to validate the forms in React JS. Did you know this method to validate the forms?
Let's Exploreπ
Before We Start
Be ready with your React JS project with a form and install Yup to validate the form. Confused??, No worries.
Step 1: Installing React JS
I am going with vite to install the React JS. Simply run the following command to install React JS.
npm create vite@latest my-react-app --template react
*replace the my-react-app with your project name.
change the directory to the project folder.
cd my-react-app
Install the required dependencies by running
npm install
Step 2: Installing Yup
Yup is a JavaScript schema builder for value parsing and validation. It is often used to validate data structures such as objects and arrays, ensuring that they conform to specified formats and constraints. Yup is particularly popular in conjunction with form libraries like Formik for handling form validation in React applications.
Install the Yup using the following command
npm install yup
As we are only learning the validation method here, I am not going for a detailed explanation of Yup. if you are new to the Yup, here is docs for Yup.
Step 3: Set up a form
I am creating my form inside App.jsx and Here it is.
import { useState } from "react";
const App = () => {
const [isPasswordHide, setIsPasswordHide] = useState(true);
const [loginDetails, setLoginDetails] = useState({
email: "",
password: "",
});
const toggleType = () => setIsPasswordHide(!isPasswordHide);
const handleChange = (event) => {
setLoginDetails({
...loginDetails,
[event.target.name]: event.target.value,
});
};
const handleLogin = (event) => {
event.preventDefault()
// Submit the form
};
return (
<div className="w-full h-screen flex justify-center items-center ">
<div className="w-[500px] h-[555px] px-[80px] py-[60px] rounded-[10px] border shadow-black/[12%] shadow-[1px_1px_5px_1px]">
<form onSubmit={handleLogin}>
<div className="">
<h1 className="text-[40px] text-text-secondary font-semibold">
Login
</h1>
<p className=" text-text-secondary/70 tracking-[1.5px] font-medium">
Please Login to your account
</p>
</div>
<div className="mt-[40px]">
<div className="input-container mb-4">
<label htmlFor="" className="label">
Email Address
</label>
<input
type="text"
className="input"
placeholder="Enter your email"
name="email"
id="email"
onChange={handleChange}
value={loginDetails.email}
/>
</div>
<div className="input-container">
<label htmlFor="" className="label">
Password
</label>
<div
className="input "
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}>
<input
type={isPasswordHide ? "password" : "text"}
placeholder="Enter password"
className="outline-none"
name="password"
id="password"
onChange={handleChange}
value={loginDetails.password}
/>
<button type="button" onClick={toggleType}>
{isPasswordHide ? "SHOW" : "HIDE"}
</button>
</div>
</div>
<button
type="submit"
className="w-full mt-[60px] py-[12px] px-[12px] bg-[#F76279] text-white rounded-[8px]">
Login
</button>
</div>
</form>
</div>
</div>
);
};
export default App;
Never mind the lengthyyy className, Using Tailwind CSS to style. Style your form, make it beautiful as per your creativityπ.
Coolπ, We are Good To Go!!!.
Validation Starts Here
I am going to create two reusable/common functions, one is for validating the form another is for resetting the errors.
Create a folder called utils
in the src
directory and create a file validator.js
inside the utils
.
So, the folder structure will be like this
my-react-app/
βββ node_modules/
βββ public/
β βββ index.html
βββ src/
β βββ utils/
β β βββ validator.js
β βββ App.jsx
β βββ main.jsx
β βββ index.css
βββ .gitignore
βββ index.html
βββ package.json
βββ vite.config.js
βββ README.md
The validator.js
will have the following functions
const createError = (id, message) => {
const errorElementExist = document.querySelector(`#${id}_error`);
if (!errorElementExist) {
const errorElement = document.createElement("span");
errorElement.id = `${id}_error`;
errorElement.className = `error-msg`;
errorElement.innerText = message;
let inputElment = document.querySelector(`#${id}`);
let inputParentElment = document.querySelector(`#${id}`).parentElement;
if (!inputParentElment.getAttribute("data-input-parent")) {
while (
!inputParentElment.getAttribute("data-input-parent") &&
inputParentElment !== null &&
inputParentElment.nodeType === 1
) {
inputParentElment = inputParentElment.parentNode;
}
}
console.log(inputElment);
if (inputElment.getAttribute("data-input") === false) {
while (
inputElment.getAttribute("data-input") === false &&
inputElment !== null &&
inputElment.nodeType === 1
) {
inputElment = inputElment.parentNode;
}
}
inputElment.style.border = "1px solid red"
inputParentElment.appendChild(errorElement);
}
};
export const validate = async ({ validationSchema, value }) => {
let errors = {};
try {
const result = await validationSchema.validate(value, {
abortEarly: false,
});
} catch (error) {
if (error.name === "ValidationError") {
error.inner.forEach((err) => {
errors[err.path] = err.message;
createError(err.path, err.message);
});
}
}
return { errors, isValid: Object.keys(errors).length === 0 };
};
export const removeError = (id) => {
const errorElement = document.querySelector(`#${id}_error`);
let inputElment = document.querySelector(`#${id}`);
if (inputElment.getAttribute("data-input") === false) {
while (
inputElment.getAttribute("data-input") === false &&
inputElment !== null &&
inputElment.nodeType === 1
) {
inputElment = inputElment.parentNode;
}
}
// set the border according to your UI
inputElment.style.border = "1px solid grey"
// remove the span tag with error from the dom
if (errorElement) errorElement.remove();
};
Explanation
- validate: This function will receive one object as parameters, which will have two keys, validationSchema, and value. This function validates the given values against a Yup validation schema and handles the display of error messages.
- validationSchema: Yup validation schema object.
- value: It is the value to be validated.
- It initializes an empty errors object.
- It tries to validate the provided values (value) against the validationSchema with abortEarly set to false, meaning it will validate all fields before stopping.
- If validation errors occur, it catches the ValidationError and iterates over the errors.
- For each error, it adds the error message to the errors object and calls createError to display the error message.
- It returns an object containing the errors and a boolean isValid indicating if the validation passed without errors.
- createError: This function displays an error message next to a form field when a validation error occurs.
- It first checks if an error message element already exists for the given field ID. If it doesn't, it creates a new element.
- It sets the id and className of the new error element, and its inner text to the provided error message.
- It then finds the input element and its parent element. If the parent element does not have the attribute data-input-parent, it traverses up the DOM tree until it finds an element with this attribute.
- If the input element does not have the attribute data-input set to false, it also traverses up the DOM tree until it finds an appropriate element.
- Finally, it appends the error message element to the parent element of the input field.
3.removeError: This function removes the error message associated with a specific form field.
- It selects the error message element for the given field ID.
- It finds the input element and traverses up the DOM tree if necessary to find an element with the class input.
- It sets the data-invalid attribute of the input element to false, marking it as valid.
- It removes the error message element if it exists.
So, These three functions will help you to validate the values.
Now, the question is...
How to use it with form
We need to import and call the valid validate method inside the onSubmit and call the removeError, inside the handleChange.
And the component will look like this.
import { useState } from "react";
import { validate, removeError } from "./utils/validation";
import { object, string } from "yup";
const App = () => {
const [isPasswordHide, setIsPasswordHide] = useState(true);
const [loginDetails, setLoginDetails] = useState({
email: "",
password: "",
});
const toggleType = () => setIsPasswordHide(!isPasswordHide);
const handleChange = (event) => {
setLoginDetails({
...loginDetails,
[event.target.name]: event.target.value,
});
removeError(event.target.id)
};
let loginValidationSchema = object().shape({
email: string()
.required("User ID is required")
.email("Please enter a valid email address"),
password: string().required("Required"),
});
// onSudmit
const handleLogin = async (event) => {
event.preventDefault();
// Submit the form
const { errors, isValid } = await validate({
validationSchema: loginValidationSchema,
value: loginDetails,
});
// errors, isValid can be used to control the submission of form
console.log(errors);
if(!isValid) return;
// api call or any logic
};
return (
<div className="w-full h-screen flex justify-center items-center ">
<div className="w-[500px] h-[555px] px-[80px] py-[60px] rounded-[10px] border shadow-black/[12%] shadow-[1px_1px_5px_1px]">
<form onSubmit={handleLogin}>
<div className="">
<h1 className="text-[40px] text-text-secondary font-semibold">
Login
</h1>
<p className=" text-text-secondary/70 tracking-[1.5px] font-medium">
Please Login to your account
</p>
</div>
<div className="mt-[40px]">
<div className="input-container mb-4" data-input-parent="true">
<label htmlFor="" className="label">
Email Address
</label>
<input
type="text"
className="input"
placeholder="Enter your email"
name="email"
id="email"
onChange={handleChange}
value={loginDetails.email}
/>
</div>
<div className="input-container" data-input-parent="true">
<label htmlFor="" className="label">
Password
</label>
<div
className="input "
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
data-input="true"
>
<input
type={isPasswordHide ? "password" : "text"}
placeholder="Enter password"
className="outline-none"
name="password"
id="password"
onChange={handleChange}
value={loginDetails.password}
/>
<button type="button" onClick={toggleType}>
{isPasswordHide ? "SHOW" : "HIDE"}
</button>
</div>
</div>
<button
type="submit"
className="w-full mt-[60px] py-[12px] px-[12px] bg-[#F76279] text-white rounded-[8px]"
>
Login
</button>
</div>
</form>
</div>
</div>
);
};
export default App;
Conclusion
This will be one of the best methods, to handle the validations of the forms in React JS. You can avoid the copy-pasting & duplication of the code for validation and this method will allow you to reuse the code.
And, That is how I used to validate the forms. What about you?
About Me
I am Sajith P J, Senior React JS Developer, A JavaScript developer with the entrepreneurial mindset. I combine my experience with the super powers of JavaScript to satisfy your requirements.
*Reach Me Out *
Thanks !!!π
Top comments (0)