DEV Community

Cover image for Validation With Yup!. Did You Know This Method ?
Sajith P J
Sajith P J

Posted on

Validation With Yup!. Did You Know This Method ?

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
Enter fullscreen mode Exit fullscreen mode

*replace the my-react-app with your project name.

change the directory to the project folder.

cd my-react-app
Enter fullscreen mode Exit fullscreen mode

Install the required dependencies by running

npm install
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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();
};

Enter fullscreen mode Exit fullscreen mode

Explanation

  1. 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.
  1. 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;

Enter fullscreen mode Exit fullscreen mode

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)