Валидация данных в формах чаще всего выносится в отдельный слой,
чтобы отделить бизнес-логику от UI-компонентов. В экосистеме React
наиболее распространённой комбинацией является использование React Hook
Form совместно с Yup-схемами через yupResolver. Такой
подход позволяет описывать правила валидации декларативно и
минимизировать количество перерендеров формы.
Ключевая идея интеграции заключается в том, что
react-hook-form не выполняет валидацию сам по себе, а
делегирует её резолверу. YupResolver выступает адаптером
между схемой Yup и API формы.
Для работы требуется три основных пакета:
react-hook-formyup@hookform/resolversУстановка:
npm install react-hook-form yup @hookform/resolvers
или
yarn add react-hook-form yup @hookform/resolvers
Yup позволяет описывать структуру данных и правила проверки через цепочку методов.
Пример базовой схемы:
import * as yup from "yup";
const schema = yup.object({
email: yup
.string()
.email("Некорректный email")
.required("Email обязателен"),
password: yup
.string()
.min(6, "Минимум 6 символов")
.required("Пароль обязателен"),
});
Важный момент: Yup возвращает не просто булево значение, а объект ошибок, который затем интерпретируется резолвером.
Основная точка интеграции — функция yupResolver, которая
передаётся в useForm.
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
Далее схема подключается в конфигурации формы:
const schema = yup.object({
email: yup.string().email().required(),
password: yup.string().min(6).required(),
});
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(schema),
});
При каждом сабмите или изменении поля (в зависимости от режима валидации) происходит следующий процесс:
yupResolverreact-hook-form обновляет
formState.errorsЭтот процесс полностью синхронизирован, но не требует ручного управления состоянием ошибок.
import React from "react";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
const schema = yup.object({
email: yup.string().email("Некорректный email").required("Обязательное поле"),
password: yup.string().min(6, "Минимум 6 символов").required("Обязательное поле"),
});
export default function LoginForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(schema),
});
const onSub mit = (data) => {
console.log("Отправленные данные:", data);
};
return (
<form onSub mit={handleSubmit(onSubmit)}>
<div>
<input placeholder="Email" {...register("email")} />
{errors.email && <p>{errors.email.message}</p>}
</div>
<div>
<input type="password" placeholder="Пароль" {...register("password")} />
{errors.password && <p>{errors.password.message}</p>}
</div>
<button type="submit">Войти</button>
</form>
);
}
react-hook-form позволяет задавать стратегию
проверки:
onSubmit — проверка только при отправке формыonBlur — проверка при потере фокусаonChange — проверка при каждом измененииall — комбинация событийПример:
useForm({
resolver: yupResolver(schema),
mode: "onChange",
});
При использовании yupResolver режим влияет только на
частоту вызова схемы, но не на её логику.
Ошибки приходят в объекте formState.errors и имеют
вложенную структуру, соответствующую полям схемы.
Пример структуры:
{
email: {
type: "validation",
message: "Некорректный email"
},
password: {
type: "min",
message: "Минимум 6 символов"
}
}
Доступ к ошибке всегда осуществляется через имя поля:
errors.email?.message
Yup поддерживает вложенные структуры, что напрямую транслируется в React Hook Form.
const schema = yup.object({
user: yup.object({
name: yup.string().required("Имя обязательно"),
age: yup.number().min(18, "Возраст должен быть >= 18"),
}),
});
Использование в форме:
<input {...register("user.name")} />
<input {...register("user.age")} />
Yup также поддерживает массивы через array().of():
const schema = yup.object({
friends: yup.array().of(
yup.object({
name: yup.string().required(),
})
),
});
В связке с useFieldArray это позволяет строить
динамические формы с полноценной валидацией.
При использовании yupResolver важно учитывать
соответствие defaultValues структуре схемы:
useForm({
resolver: yupResolver(schema),
defaultValues: {
email: "",
password: "",
},
});
Несоответствие структуры может привести к отсутствию валидации
отдельных полей или к undefined в errors.
Yup поддерживает асинхронные проверки, например запросы к API:
const schema = yup.object({
email: yup
.string()
.email()
.test("checkEmail", "Email уже занят", async (value) => {
const res = await fetch(`/api/check-email?email=${value}`);
const data = await res.json();
return !data.exists;
}),
});
yupResolver корректно обрабатывает промисы, возвращаемые
схемой, и интегрирует результат в lifecycle формы.
При использовании TypeScript можно вывести тип формы из схемы:
import { InferType } from "yup";
type FormData = InferType<typeof schema>;
И передать его в useForm:
const form = useForm<FormData>({
resolver: yupResolver(schema),
});
Это обеспечивает синхронизацию типов между UI и валидацией.
defaultValues и схемыregister без совпадения ключей Yupuser.name vs
userName)yupResolver выполняет полную проверку схемы при каждом
запуске. При больших формах это может быть затратным.
Для оптимизации применяются:
mode: "onBlur" вместо onChangeuseMemo при динамическом
создании