Внедрение проверки формы в React с нуля может стать очень запутанным, особенно когда вам нужно одновременно проверять различные типы ввода, обрабатывать ошибки и отключать кнопки.
Вот почему существуют библиотеки, которые уже обрабатывают все это за нас. В этом уроке я научу вас, как использовать библиотеки react-hook-form
и zod
для реализации проверки формы.
Мы будем использовать TypeScript для этого урока.
Настраивать
Начнем с инициализации нового приложения React с помощью Vite. Я буду использовать Tailwind CSS для стилизации.
Создайте новый проект с помощью Vite.
npm create vite@latest
Выберите React и Typescript.
Need to install the following packages: create-vite@latest Ok to proceed? (y) y √ Project name: ... react_form_validation √ Select a framework: » React √ Select a variant: » TypeScript
Перейдите в каталог проекта и установите зависимости.
cd react_form_validation npm install npm run dev
Установите и инициализируйте Tailwind.
npm install -D tailwindcss postcss autoprefixer npx tailwindcss init -p
Настройте файл конфигурации Tailwind.
module.exports = { content: [ "./src/**/*.{js,jsx,ts,tsx}", ], theme: { extend: {}, }, plugins: [], }
Добавьте это в файл index.css
@tailwind base; @tailwind components; @tailwind utilities;
Запустите сервер и перейдите на http://127.0.0.1:5173/
, вы должны увидеть эту страницу.
Теперь давайте установим пакеты, необходимые для проекта.
npm install react-hook-form @hookform/resolvers zod
Очистите файл App.tsx
и добавьте следующий код Tailwind для регистрационной формы.
function App() { return ( <section className="bg-gray-50 dark:bg-gray-900"> <div className="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0"> <div className="w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700"> <div className="p-6 space-y-4 md:space-y-6 sm:p-8"> <h1 className="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white"> Create and account </h1> <form className="space-y-4 md:space-y-6"> <div> <label htmlFor="username" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" > Your username </label> <input type="text" id="username" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5" placeholder="Your name" /> </div> <div> <label htmlFor="email" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" > Your email </label> <input type="email" id="email" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5" placeholder="[email protected]" /> </div> <div> <label htmlFor="password" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" > Password </label> <input type="password" id="password" placeholder="••••••••" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5" /> </div> <div> <label htmlFor="confirm-password" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" > Confirm password </label> <input type="password" id="confirmPassword" placeholder="••••••••" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5" /> </div> <div className="flex items-start"> <div className="flex items-center h-5"> <input id="terms" aria-describedby="terms" type="checkbox" className="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-primary-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-primary-600 dark:ring-offset-gray-800" /> </div> <div className="ml-3 text-sm"> <label htmlFor="terms" className="font-light text-gray-500 dark:text-gray-300" > I accept the{" "} <a className="font-medium text-primary-600 hover:underline dark:text-primary-500" href="#" > Terms and Conditions </a> </label> </div> </div> <button type="submit" className="w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800" > Create an account </button> </form> </div> </div> </div> </section> ); } export default App;
Добавьте следующее в файл конфигурации Tailwind для цветов.
module.exports = { content: [ "./src/**/*.{js,jsx,ts,tsx}", ], theme: { extend: { colors: { primary: { "50": "#eff6ff", "100": "#dbeafe", "200": "#bfdbfe", "300": "#93c5fd", "400": "#60a5fa", "500": "#3b82f6", "600": "#2563eb", "700": "#1d4ed8", "800": "#1e40af", "900": "#1e3a8a" } } }, }, plugins: [], }
Вы должны увидеть эту форму в браузере.
Создание схемы проверки с помощью Zod
Во-первых, давайте импортируем все необходимые пакеты.
import { z } from "zod"; import { SubmitHandler, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod";
Я собираюсь кратко объяснить, как работает Зод. Zod — это первая библиотека Typescript для создания схем проверки. Это означает, что вы можете определить, как должны выглядеть ваши данные при использовании типов Typescript. Zod может автоматически создавать типы из схемы, а также не имеет зависимостей.
Давайте начнем с создания схемы для нашей формы.
const formSchema = z .object({ username: z.string().min(1, "Username is required").max(100), email: z.string().email("Invalid email").min(1, "Email is required"), password: z .string() .min(1, "Password is required") .min(8, "Password must have more than 8 characters"), confirmPassword: z.string().min(1, "Password confirmation is required"), terms: z.literal(true, { errorMap: () => ({ message: "You must accept the terms and conditions" }), }), }) .refine((data) => data.password === data.confirmPassword, { path: ["confirmPassword"], message: "Passwords do not match", });
Во-первых, мы объявили новую схему, используя z.object()
. Здесь мы начинаем с определения типов нашего поля, в данном случае все они являются строками, поэтому мы используем z.string()
. Затем мы используем функцию z.min()
, чтобы указать, что нам не нужна пустая строка, также мы можем добавить сообщение об ошибке в качестве второго аргумента. Для электронной почты мы можем использовать функцию z.email()
, которая поставляется с Zod.
Мы добавляем еще один z.min()
для пароля, чтобы указать, что мы хотим, чтобы в нем было не менее 8 символов, и мы устанавливаем поле терминов как z.literal()
, что означает, что это поле должно быть точно заданным значением.
Наконец, мы используем метод refine
для реализации пользовательской проверки соответствия паролей. С помощью этого метода вы также можете реализовать настраиваемые сообщения об ошибках и более сложные проверки.
Теперь мы можем создать тип для нашей схемы, используя метод Zod infer
. Мы будем использовать этот тип, чтобы сообщить react-hook-form
, как должны выглядеть наши данные.
type FormSchemaType = z.infer<typeof formSchema>;
Реализация формы React Hook
Эта библиотека поставляется с пользовательским хуком с именем useForm
, это позволит нам регистрировать наши входные данные, обрабатывать отправку формы и обрабатывать ошибки.
const { register, handleSubmit, formState: { errors, isSubmitting }, } = useForm<FormSchemaType>({ resolver: zodResolver(formSchema), });
Здесь мы деструктурировали 4 свойства из хука. Мы будем использовать register
, чтобы сообщить react-hook-form
, какие входные данные нужно проверять, handleSubmit
— для обработки отправки формы, errors
— это объект, который будет содержать все ошибки формы, а isSubmitting
содержит логическое значение, которое мы можем использовать, чтобы проверить, обрабатывается ли форма в данный момент. поданный.
Мы также передаем преобразователь в хук, в этом случае мы используем нашу схему Zod для проверки формы.
Теперь давайте создадим функцию для обработки отправки формы.
const onSubmit: SubmitHandler<FormSchemaType> = (data) => { console.log(data); };
Эта функция будет вызываться при срабатывании метода onSubmit
нашей формы. Пока мы просто будем записывать данные в консоль.
Регистрация полей
Мы можем использовать метод register
, чтобы сообщить react-hook-form
, какие поля проверять.
<input type="text" id="username" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5" placeholder="Your name" {...register("username")} />
Убедитесь, что поля имеют те же значения, что и в схеме Zod. Ваша форма должна выглядеть так.
<form className="space-y-4 md:space-y-6"> <div> <label htmlFor="username" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" > Your username </label> <input type="text" id="username" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5" placeholder="Your name" {...register("username")} /> </div> <div> <label htmlFor="email" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" > Your email </label> <input type="email" id="email" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5" placeholder="[email protected]" {...register("email")} /> </div> <div> <label htmlFor="password" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" > Password </label> <input type="password" id="password" placeholder="••••••••" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5" {...register("password")} /> </div> <div> <label htmlFor="confirmPassword" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" > Confirm password </label> <input type="password" id="confirmPassword" placeholder="••••••••" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5" {...register("confirmPassword")} /> </div> <div className="flex items-start"> <div className="flex items-center h-5"> <input id="terms" aria-describedby="terms" type="checkbox" className="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-primary-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-primary-600 dark:ring-offset-gray-800" {...register("terms")} /> </div> <div className="ml-3 text-sm"> <label htmlFor="terms" className="font-light text-gray-500 dark:text-gray-300" > I accept the{" "} <a className="font-medium text-primary-600 hover:underline dark:text-primary-500" href="#" > Terms and Conditions </a> </label> </div> </div> <button type="submit" className="w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800" > Create an account </button> </form>
Отправить обработчик
С помощью метода handleSubmit
мы можем вызвать обработчик onSubmit
, передав его в форму.
<form className="space-y-4 md:space-y-6" onSubmit={handleSubmit(onSubmit)} >
Теперь, когда вы заполните форму и нажмете кнопку отправки, вы сможете увидеть данные формы в консоли.
Следует отметить, что форма может быть отправлена несколько раз, если вы нажмете кнопку отправки много раз подряд. Давайте исправим это, отключив кнопку отправки во время обработки нашей формы.
<button type="submit" className="w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800" disabled={isSubmitting} > Create an account </button>
Вы также можете добавить стиль к кнопке, используя свойство disabled
, которое есть у Tailwind.
Обработка ошибок
Последнее, что нужно сделать, это обработать ошибки, для этого мы можем использовать объект errors.
<div> <label htmlFor="email" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" > Your email </label> <input type="email" id="email" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5" placeholder="[email protected]" {...register("email")} /> {errors.email && ( <span className="text-red-800 block mt-2"> {errors.email?.message} </span> )} </div>
Здесь мы проверяем, находится ли поле электронной почты в объекте ошибок. Если он есть, то мы отображаем сообщение под полем.
Ваша форма должна выглядеть так.
<form className="space-y-4 md:space-y-6" onSubmit={handleSubmit(onSubmit)} > <div> <label htmlFor="username" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" > Your username </label> <input type="text" id="username" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5" placeholder="Your name" {...register("username")} /> {errors.username && ( <span className="text-red-800 block mt-2"> {errors.username?.message} </span> )} </div> <div> <label htmlFor="email" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" > Your email </label> <input type="email" id="email" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5" placeholder="[email protected]" {...register("email")} /> {errors.email && ( <span className="text-red-800 block mt-2"> {errors.email?.message} </span> )} </div> <div> <label htmlFor="password" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" > Password </label> <input type="password" id="password" placeholder="••••••••" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5" {...register("password")} /> {errors.password && ( <span className="text-red-800 block mt-2"> {errors.password?.message} </span> )} </div> <div> <label htmlFor="confirmPassword" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" > Confirm password </label> <input type="password" id="confirmPassword" placeholder="••••••••" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5" {...register("confirmPassword")} /> {errors.confirmPassword && ( <span className="text-red-800 block mt-2"> {errors.confirmPassword?.message} </span> )} </div> <div className="flex items-start"> <div className="flex items-center h-5"> <input id="terms" aria-describedby="terms" type="checkbox" className="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-primary-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-primary-600 dark:ring-offset-gray-800" {...register("terms")} /> </div> <div className="ml-3 text-sm"> <label htmlFor="terms" className="font-light text-gray-500 dark:text-gray-300" > I accept the{" "} <a className="font-medium text-primary-600 hover:underline dark:text-primary-500" href="#" > Terms and Conditions </a> </label> </div> </div> {errors.terms && ( <span className="text-red-800 block mt-2"> {errors.terms?.message} </span> )} <button type="submit" className="w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800" disabled={isSubmitting} > Create an account </button> </form>
Тестирование формы
Теперь мы можем проверить нашу форму.
Если мы попытаемся отправить форму, не заполнив ее, мы увидим, что появляются необходимые сообщения об ошибках.
Проверка пароля тоже работает.
И наши данные записываются в консоль, как и ожидалось.
Заключение
Теперь вы знаете, как реализовать проверку формы в React с помощью библиотек react-hook-form
и zod
. Я надеюсь, что теперь вы видите ценность использования библиотек проверки формы и то, как они помогают нам экономить время и улучшать наш код. Теперь, если вам нужно повторно использовать логику проверки формы, вы можете перевести ее в Bit, чтобы вы могли использовать ее позже или поделиться ею с товарищами по команде.
Я оставляю здесь ссылку на репозиторий GitHub, если вы хотите проверить готовый код.
Спасибо за чтение!
Создавайте приложения с многократно используемой логикой
Инструмент с открытым исходным кодом Bit помогает более чем 250 000 разработчиков создавать приложения с компонентами.
Превратите любой пользовательский интерфейс, функцию или страницу в компонент многократного использования — и поделитесь им со своими приложениями. Легче сотрудничать и строить быстрее.
Разделите приложения на компоненты, чтобы упростить разработку приложений, и наслаждайтесь наилучшими возможностями для рабочих процессов, которые вы хотите: