Связка Yup и React Hook Form строится вокруг концепции внешней
схемной валидации, где React Hook Form отвечает за управление состоянием
формы, а Yup — за декларативное описание правил проверки данных.
Связующим слоем выступает YupResolver из пакета
@hookform/resolvers, который преобразует Yup-валидацию в
формат, понимаемый React Hook Form.
Ключевая идея заключается в том, что React Hook Form не выполняет синхронную проверку значений сам по себе в момент изменения поля. Вместо этого он делегирует проверку резолверу, а тот уже вызывает Yup-схему и возвращает структурированный результат: валидные данные либо набор ошибок.
При каждом триггере валидации (submit, change, blur — в зависимости
от режима mode) React Hook Form передаёт текущие значения
формы в функцию резолвера. YupResolver выполняет следующие шаги:
Принимает объект значений формы.
Передаёт его в schema.validate или
schema.validateSync (в зависимости от
конфигурации).
Обрабатывает результат:
{ values, errors: {} }Важный аспект: Yup возвращает ошибки в виде дерева исключений, тогда как React Hook Form ожидает плоскую или вложенную структуру с привязкой к именам полей.
Основной способ подключения строится через 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(8).required()
});
Далее схема передаётся в резолвер:
const {
register,
handleSubmit,
formState: { errors }
} = useForm({
resolver: yupResolver(schema)
});
На этом уровне YupResolver становится промежуточным слоем между UI и правилами валидации.
Yup генерирует ошибки в формате ValidationError, который
содержит:
path — путь к полю (например,
user.email)message — текст ошибкиinner — массив вложенных ошибок при
object().shapeYupResolver преобразует это в структуру React Hook Form:
{
email: {
type: "validation",
message: "Invalid email"
}
}
Для вложенных объектов:
{
user: {
email: {
message: "Required"
}
}
}
Это преобразование критично для корректного отображения ошибок в UI без дополнительной нормализации со стороны разработчика.
Yup особенно полезен при работе с вложенными объектами и массивами:
const schema = yup.object({
user: yup.object({
profile: yup.object({
firstName: yup.string().required(),
lastName: yup.string().required()
})
})
});
React Hook Form при этом использует точечную нотацию:
register("user.profile.firstName");
YupResolver сохраняет соответствие между вложенной структурой Yup и плоскими ключами RHF, обеспечивая согласованность данных.
При использовании yup.array() появляется необходимость
синхронизации динамических форм:
const schema = yup.object({
users: yup.array().of(
yup.object({
name: yup.string().required(),
age: yup.number().min(18)
})
)
});
React Hook Form управляет такими структурами через
useFieldArray, а YupResolver обеспечивает валидацию каждого
элемента массива.
Особенность заключается в том, что ошибки возвращаются с индексной адресацией:
users[0].name
users[2].age
Это позволяет точно локализовать ошибки в UI-списках.
React Hook Form поддерживает несколько режимов:
onSubmitonChangeonBlurallYupResolver вызывается каждый раз при срабатывании выбранного режима. Это влияет на нагрузку, особенно при сложных схемах.
При onChange схема может вызываться десятки раз в
секунду, что делает важным оптимизацию Yup:
abortEarly: false только при
необходимостиvalidateSync в простых формахYup поддерживает асинхронные проверки через test:
yup.string().test(
"checkEmail",
"Email already exists",
async (value) => {
return await api.checkEmail(value);
}
);
YupResolver автоматически поддерживает async-схемы, возвращая Promise. React Hook Form в этом случае переходит в режим ожидания результата валидации.
Это важно для сценариев:
Yup позволяет централизованно управлять сообщениями:
yup.string().required("Поле обязательно")
Однако в крупных приложениях часто используется трансляция ошибок
через setLocale:
yup.setLocale({
mixed: {
required: "Обязательное поле"
}
});
YupResolver при этом не вмешивается в текст сообщений, а только транспортирует их в RHF.
В TypeScript связка приобретает дополнительную строгость:
import { InferType } from "yup";
const schema = yup.object({
email: yup.string().email().required(),
age: yup.number().required()
});
type FormData = InferType<typeof schema>;
Использование:
useForm<FormData>({
resolver: yupResolver(schema)
});
Это обеспечивает согласованность между схемой и типами формы без дублирования интерфейсов.
YupResolver добавляет слой абстракции, который влияет на производительность в зависимости от сложности схемы:
testwhen() вместо сложных условных
конструкций в JSReact Hook Form сам по себе минимизирует перерендеры, но YupResolver выполняется при каждой валидации, поэтому схема должна быть стабильной.
Yup поддерживает динамические правила:
yup.object({
isCompany: yup.boolean(),
companyName: yup.string().when("isCompany", {
is: true,
then: (schema) => schema.required()
})
});
YupResolver просто исполняет результат схемы, не вмешиваясь в логику ветвления. Это позволяет строить декларативные формы без условных операторов в React.
React Hook Form может передавать неполные данные (например, при инициализации формы). YupResolver в этом случае:
yup.string().default("guest")
Ошибки, возвращаемые через formState.errors,
используются напрямую:
{errors.email?.message}
YupResolver гарантирует, что структура ошибок:
В экосистеме React Hook Form существуют альтернативы:
YupResolver отличается:
При этом Yup менее строг в типизации по сравнению с Zod, что
компенсируется TypeScript-обвязкой через InferType.
Если Yup-схема некорректна или выбрасывает исключение вне
ValidationError, YupResolver:
Это важно учитывать при использовании кастомных test,
где возможны непредвиденные исключения.
React Hook Form хранит состояние независимо от Yup. Это означает:
YupResolver выполняет роль синхронизатора между двумя независимыми системами: state management и validation engine.