Валидация формы с помощью Yup и React Hook form

Привет всем, сегодня я расскажу, как добавить валидацию формы в наше React-приложение с помощью Yup и react hook form.

Видео версия
Видеоверсия youtube

Финальная демонстрация

Приступая к работе, я создал новый проект react и установил tailwind css.
Если вы хотите добавить tailwind css в свой проект react, пожалуйста, следуйте этому руководству tailwindcss-react.

Вот что у меня есть на данный момент:
App.js

function App() {
  return <div className="w-screen h-screen bg-gradient-to-r from-blue-900 to-purple-900 grid place-content-center">

  </div>;
}

export default App;

Вход в полноэкранный режим Выход из полноэкранного режима

Далее давайте создадим папку components, в которой будет храниться наш компонент Form

src/components/Form/Form.jsx

На этом этапе мы создадим наш компонент формы и разместим в нем различные входы формы

const textInputClassName =
  "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500";

const Form = () => {
  return (
    <div className="md:w-[500px] shadow-sm shadow-white bg-white w-[320px] mx-auto px-7 py-4 rounded-xl">
      <form className="w-full">
        <div className="mb-6">
          <label
            htmlFor="email"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Your email
          </label>
          <input
            type="email"
            id="email"
            className={textInputClassName}
            placeholder="test@test.com"
          />
        </div>
        <div className="mb-6">
          <label
            htmlFor="password"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Your password
          </label>
          <input type="password" id="password" className={textInputClassName} />
        </div>
        <div className="mb-6">
          <label
            htmlFor="confirmPassword"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Confirm Password
          </label>
          <input
            type="password"
            id="confirmPassword"
            className={textInputClassName}
          />
        </div>
        <div className="mb-6">
          <label
            htmlFor="accountType"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400"
          >
            Select an option
          </label>
          <select
            id="accountType"
            className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
          >
            <option value="">Account Type</option>
            <option value="personal">Personal</option>
            <option value="commercial">Commercial</option>
          </select>
        </div>
        <div className="flex justify-between mb-6">
          <div className="flex">
            <div className="flex items-center h-5">
              <input
                id="remember"
                type="checkbox"
                value=""
                className="w-4 h-4 bg-gray-50 rounded border border-gray-300 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"
              />
            </div>
            <label
              htmlFor="remember"
              className="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300"
            >
              Remember me
            </label>
          </div>

          <div>
            <label
              htmlFor="default-toggle"
              className="inline-flex relative items-center cursor-pointer"
            >
              <input
                type="checkbox"
                value=""
                id="default-toggle"
                className="sr-only peer"
              />
              <div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
              <span className="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300">
                Toggle me
              </span>
            </label>
          </div>
        </div>

        <button
          type="submit"
          className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
        >
          Submit
        </button>
      </form>
    </div>
  );
};

export default Form;

Вход в полноэкранный режим Выход из полноэкранного режима

Теперь мы закончили с нашим компонентом формы JSX, давайте продолжим и добавим нашу форму в App.js

import Form from "./components/Form/Form";

function App() {
  return (
    <div className="w-screen h-screen bg-gradient-to-r from-blue-900 to-purple-900 grid place-content-center">
      <Form />
    </div>
  );
}

export default App;

Вход в полноэкранный режим Выйти из полноэкранного режима

Теперь наш App.js выглядит следующим образом, что дает нам такой результат


Теперь у нас есть дизайн нашей формы, давайте перейдем к добавлению валидации. Нам необходимо установить следующие пакеты

npm install -D yup @hookform/resolvers react-hook-form
или в случае, если вы используете yarn

Yup будет нашим конструктором схем для разбора значений и валидации,
React-hook-form поможет нам проверить вводимые данные формы,
@hookform/resolvers используется для интеграции yup и react-hook-form.

Давайте импортируем пакеты, которые мы только что установили, в наш компонент Form

/components/Form/Form.jsx

import { useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
Вход в полноэкранный режим Выйти из полноэкранного режима

Прежде чем мы начнем строить нашу схему ValidationSchema, нам нужно добавить атрибут name к нашей html-форме, так как это важно для того, чтобы yup и react-hook-form могли отслеживать наши различные вводимые данные.

   <form className="w-full">
        <div className="mb-6">
          <label
            htmlFor="email"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Your email
          </label>
          <input
            type="email"
            name="email"
            id="email"
            className={textInputClassName}
            placeholder="test@test.com"
          />
        </div>
        <div className="mb-6">
          <label
            htmlFor="password"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Your password
          </label>
          <input type="password" id="password" className={textInputClassName} />
        </div>
        <div className="mb-6">
          <label
            htmlFor="confirmPassword"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Confirm Password
          </label>
          <input
            name="password"
            type="password"
            id="confirmPassword"
            className={textInputClassName}
          />
        </div>
        <div className="mb-6">
          <label
            htmlFor="accountType"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400"
          >
            Select an option
          </label>
          <select
            name="accountType"
            id="accountType"
            className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
          >
            <option value="">Account Type</option>
            <option value="personal">Personal</option>
            <option value="commercial">Commercial</option>
          </select>
        </div>
        <div className="flex justify-between mb-6">
          <div className="flex">
            <div className="flex items-center h-5">
              <input
                id="remember"
                name="remember"
                type="checkbox"
                value=""
                className="w-4 h-4 bg-gray-50 rounded border border-gray-300 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"
              />
            </div>
            <label
              htmlFor="remember"
              className="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300"
            >
              Remember me
            </label>
          </div>

          <div>
            <label
              htmlFor="toggle"
              className="inline-flex relative items-center cursor-pointer"
            >
              <input
                type="checkbox"
                name="toggle"
                value=""
                id="toggle"
                className="sr-only peer"
              />
              <div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
              <span className="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300">
                Accept
              </span>
            </label>
          </div>
        </div>

        <button
          type="submit"
          className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
        >
          Submit
        </button>
      </form>
Вход в полноэкранный режим Выход из полноэкранного режима

Давайте создадим нашу схему валидации, для этого я создам новую папку schema и внутри нее файл formSchema.js.

Напишем нашу схему formSchema, вот так

import * as yup from "yup";

export const registerSchema = yup.object().shape({
  email: yup
    .string("email should be a string")
    .email("please provide a valid email address")
    .required("email address is required"),
});
Вход в полноэкранный режим Выйти из полноэкранного режима

Ключ email должен совпадать с атрибутом name в вашем jsx.

В нашем Form.js

// Saving space

const Form = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: yupResolver(registerSchema),
  });

// Saving space
}
Войти в полноэкранный режим Выйти из полноэкранного режима

register будет использоваться для регистрации наших входов в react-hook-form,
handleSubmit должен быть добавлен к нашей форме onSubmit и когда мы отправляем нашу форму, поможет использовать валидацию нашей формы,
formState поможет нам отслеживать состояние нашей формы, в данном случае состояние ошибки.

Давайте добавим это к нашему входу email, пожалуйста, обратите внимание на {...register("email")} и ошибку jsx

<label
            htmlFor="email"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Your email
          </label>
          <input
            {...register("email")}
            type="email"
            name="email"
            id="email"
            className={textInputClassName}
            placeholder="test@test.com"
          />
          {errors.email ? (
            <span className="text-red-900">{errors.email.message}</span>
          ) : (
            <></>
          )}
Вход в полноэкранный режим Выйти из полноэкранного режима

в обработчике отправки нашей формы добавим следующее

<form onSubmit={handleSubmit(formSubmitHandler)} className="w-full">
// saving space
</from
Ввести полноэкранный режим Выйти из полноэкранного режима

Вы заметите, что мы передали formSubmitHandler, которая является нашей пользовательской функцией, которой будут переданы данные формы автоматически, если валидация пройдена

  const formSubmitHandler = (data) => {
    console.log(data);
  };
Войти в полноэкранный режим Выйти из полноэкранного режима

Таким образом, наша валидация формы уже работает, и мы должны получить результат, подобный этому

проверка пароля и подтверждение пароля

Давайте добавим следующее в наш файл схемы

export const registerSchema = yup.object().shape({
  email: yup
    .string("email should be a string")
    .email("please provide a valid email address")
    .required("email address is required"),
  password: yup
    .string("password should be a string")
    .min(5, "password should have a minimum length of 5")
    .max(12, "password should have a maximum length of 12")
    .required("password is required"),
  confirmPassword: yup
    .string("password should be a string")
    .oneOf([yup.ref("password")])
    .required("confirm password is required"),
});

Войти в полноэкранный режим Выйти из полноэкранного режима

Вернемся к нашему Form.js, обновим пароль и подтверждение пароля до следующего вида

<div className="mb-6">
          <label
            htmlFor="password"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Your password
          </label>
          <input
            {...register("password")}
            type="password"
            name="password"
            id="password"
            className={textInputClassName}
          />
          {errors.password ? (
            <span className="text-red-900">{errors.password.message}</span>
          ) : (
            <></>
          )}
        </div>
        <div className="mb-6">
          <label
            htmlFor="confirmPassword"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Confirm Password
          </label>
          <input
            {...register("confirmPassword")}
            name="confirmPassword"
            type="password"
            id="confirmPassword"
            className={textInputClassName}
          />
          {errors.confirmPassword ? (
            <span className="text-red-900">{errors.confirmPassword.message}</span>
          ) : (
            <></>
          )}
        </div>
Вход в полноэкранный режим Выйти из полноэкранного режима

Это дает нам следующий результат

Валидация Select

Давайте обновим наш файл схемы следующим образом

import * as yup from "yup";

export const registerSchema = yup.object().shape({
  email: yup
    .string("email should be a string")
    .email("please provide a valid email address")
    .required("email address is required"),
  password: yup
    .string("password should be a string")
    .min(5, "password should have a minimum length of 5")
    .max(12, "password should have a maximum length of 12")
    .required("password is required"),
  confirmPassword: yup
    .string("password should be a string")
    .oneOf([yup.ref("password")])
    .required("confirm password is required"),
  accountType: yup
    .string("account type should be a string")
    .oneOf(["personal", "commercial"])
    .required("account type is required"),
});

Войти в полноэкранный режим Выход из полноэкранного режима

Теперь давайте также обновим наш select jsx

<div className="mb-6">
          <label
            htmlFor="accountType"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400"
          >
            Select an option
          </label>
          <select
            {...register("accountType")}
            name="accountType"
            id="accountType"
            className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
          >
            <option value="">Account Type</option>
            <option value="personal">Personal</option>
            <option value="commercial">Commercial</option>
          </select>{" "}
          {errors.accountType ? (
            <span className="text-red-900">{errors.accountType.message}</span>
          ) : (
            <></>
          )}
        </div>
Вход в полноэкранный режим Выйти из полноэкранного режима

Теперь у нас есть вот это

Наконец, давайте проверим наши переключатели и флажки.

Начнем с обновления нашего файла схемы

import * as yup from "yup";

export const registerSchema = yup.object().shape({
  email: yup
    .string("email should be a string")
    .email("please provide a valid email address")
    .required("email address is required"),
  password: yup
    .string("password should be a string")
    .min(5, "password should have a minimum length of 5")
    .max(12, "password should have a maximum length of 12")
    .required("password is required"),
  confirmPassword: yup
    .string("password should be a string")
    .oneOf([yup.ref("password")])
    .required("confirm password is required"),
  accountType: yup
    .string("account type should be a string")
    .oneOf(["personal", "commercial"])
    .required("account type is required"),
  remember: yup.boolean().oneOf([true], "Please tick checkbox"),
  toggle: yup.boolean().oneOf([true], "Please toggle accept"),
});

Вход в полноэкранный режим Выйти из полноэкранного режима

Затем обновим jsx флажка и переключения.

<div className="flex justify-between mb-6">
          <div>
            <div className="flex">
              <div className="flex items-center h-5">
                <input
                  {...register("remember")}
                  id="remember"
                  name="remember"
                  type="checkbox"
                  value=""
                  className="w-4 h-4 bg-gray-50 rounded border border-gray-300 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"
                />
              </div>
              <label
                htmlFor="remember"
                className="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300"
              >
                Remember me
              </label>
            </div>
            {errors.remember ? (
              <span className="text-red-900">{errors.remember.message}</span>
            ) : (
              <></>
            )}
          </div>

          <div>
            <div>
              <label
                htmlFor="toggle"
                className="inline-flex relative items-center cursor-pointer"
              >
                <input
                  {...register("toggle")}
                  type="checkbox"
                  name="toggle"
                  value=""
                  id="toggle"
                  className="sr-only peer"
                />
                <div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
                <span className="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300">
                  Accept
                </span>
              </label>
            </div>
            {errors.toggle ? (
              <span className="text-red-900">{errors.toggle.message}</span>
            ) : (
              <></>
            )}
          </div>
        </div>
Войти в полноэкранный режим Выйти из полноэкранного режима

С этим мы закончили с таким результатом

Спасибо за внимание, если вам нужен окончательный код, вот репозиторий на github

ссылка на github repo

Пожалуйста, свяжитесь со мной
linkedIn
youtube
Udemy

Оцените статью
devanswers.ru
Добавить комментарий