Validation With Yup!. Did You Know This Method ?
Tags: javascript, learning, reactjsdevelopment, frontend
Published: 2024-05-27 20:24:21

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](https://www.npmjs.com/package/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](https://www.npmjs.com/package/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**
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.

2. **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 <span> 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 **

- [LinkedIn](https://www.linkedin.com/in/sajith-p-j/)
- [Instagram](https://www.instagram.com/dev_.sajith/)
- [Website](https://sajith.in/)
- [Email (sajithpjofficialme@gmail.com)](mailto:sajithpjofficialme@gmail.com)

Thanks !!!🙌