Ошибки валидации, возвращаемые через YupResolver,
формируют центральный механизм обратной связи формы с пользователем. Их
корректное отображение в интерфейсе определяет не только удобство работы
с формой, но и предсказуемость поведения всей системы ввода данных.
YupResolver преобразует результат проверки схемы Yup в
объект, совместимый с механизмом управления формами (чаще всего React
Hook Form). Основная структура ошибок имеет вид:
{
fieldName: {
type: "validationType",
message: "Текст ошибки"
}
}
Для вложенных объектов структура становится иерархической:
{
user: {
email: {
message: "Некорректный email",
type: "email"
}
}
}
Массивы формируют индексированную структуру:
{
users: [
{
email: {
message: "Обязательное поле",
type: "required"
}
}
]
}
Эта модель требует от интерфейса способности корректно обходить вложенные структуры без потери контекста поля.
В типичной интеграции с React Hook Form ошибки извлекаются через
formState.errors:
const {
register,
formState: { errors }
} = useForm({
resolver: yupResolver(schema)
});
Каждое поле получает доступ к собственной ошибке напрямую по ключу:
<input {...register("email")} />
<p>{errors.email?.message}</p>
Ключевой момент заключается в том, что отсутствие ошибки выражается
как undefined, а не как пустая строка или объект. Это
влияет на логику рендера: любое условное отображение должно проверять
наличие поля через optional chaining или явное сравнение.
Наиболее распространённая модель интерфейса — привязка ошибки к конкретному input-компоненту.
const EmailField = ({ register, error }) => (
<div>
<input {...register("email")} />
{error && <span>{error.message}</span>}
</div>
);
Здесь важно учитывать, что error.message является
финальным текстом, сформированным Yup. Это означает, что вся логика
генерации текста отделена от UI, а компонент лишь отображает
результат.
Ошибки часто используются не только для вывода текста, но и для изменения состояния элемента:
<input
{...register("email")}
className={errors.email ? "input error" : "input"}
/>
Такой подход обеспечивает визуальную связь между состоянием валидации и интерфейсом. Однако важно избегать чрезмерного дублирования состояния, когда ошибка одновременно влияет на текст, цвет, и дополнительные индикаторы без единой системы правил.
При работе с объектами и массивами доступ к ошибкам становится менее прямым. Например:
errors.user?.address?.street?.message
Такая конструкция чувствительна к структуре формы. Любое
несоответствие пути приводит к undefined, поэтому в сложных
формах применяется утилитный доступ:
const getError = (errors, path) =>
path.split(".").reduce((acc, key) => acc?.[key], errors);
Использование подобных функций позволяет унифицировать отображение ошибок в повторно используемых компонентах.
Для динамических списков ключевым становится индекс элемента:
users.map((user, index) => (
<div key={user.id}>
<input {...register(`users.${index}.email`)} />
<p>{errors.users?.[index]?.email?.message}</p>
</div>
));
Ошибка всегда привязана к конкретному индексу массива, что требует стабильной идентификации элементов списка. Использование индекса как ключа допустимо только при отсутствии перестановок элементов.
Помимо полей, YupResolver может возвращать ошибки уровня всей формы. Такие ошибки часто применяются для бизнес-логики:
{
root: {
message: "Невозможно сохранить форму",
type: "server"
}
}
В React Hook Form это обычно представлено как:
errors.root?.message
Их отображение располагается вне контекста отдельных полей:
{errors.root && (
<div className="form-error">
{errors.root.message}
</div>
)}
Ошибки не всегда отображаются сразу. Часто используется логика:
touchedFields — поле было в фокусеdirtyFields — поле изменено{errors.email && touchedFields.email && (
<span>{errors.email.message}</span>
)}
Это снижает визуальный шум при первичном рендере формы и делает интерфейс более предсказуемым.
Yup может возвращать несколько нарушений, но обычно отображается
только первая ошибка на поле. Это связано с режимом валидации
(abortEarly):
yup.string().required().min(5)
При abortEarly: true будет возвращена только первая
ошибка. При отключении:
yup.string().required().min(5).trim()
можно получить массив ошибок, но YupResolver в
стандартной интеграции агрегирует их в одно сообщение. Поэтому UI-слой
оперирует уже финализированным текстом.
Сообщения формируются в схеме Yup:
const schema = yup.object({
email: yup
.string()
.email("Введите корректный email")
.required("Email обязателен")
});
UI не должен изменять текст ошибки, но может форматировать его отображение:
<span className="error-text">
{errors.email?.message?.toUpperCase()}
</span>
При этом важно сохранять неизменной семантику сообщения, так как она может использоваться для логирования и аналитики.
Ошибки могут быть сброшены через clearErrors:
clearErrors("email");
или полностью:
clearErrors();
Это влияет на интерфейс мгновенно, поэтому очистка часто синхронизируется с пользовательскими действиями:
Помимо YupResolver, ошибки могут задаваться вручную:
setError("email", {
type: "manual",
message: "Ошибка сервера"
});
Такие ошибки отображаются тем же способом, что и валидированные, что обеспечивает единый механизм UI-рендеринга независимо от источника ошибки.
После отправки формы сервер может вернуть структурированные ошибки:
{
email: "уже существует",
password: "слишком слабый"
}
Их трансформация:
Object.entries(serverErrors).forEach(([field, message]) => {
setError(field, {
type: "server",
message
});
});
UI при этом не различает источник ошибки, если не используется
дополнительная метка type.
Корректный вывод ошибок включает привязку к accessibility-атрибутам:
<input
{...register("email")}
aria-invalid={!!errors.email}
aria-describedby="email-error"
/>
<span id="email-error">
{errors.email?.message}
</span>
Это обеспечивает корректное поведение скринридеров и улучшает навигацию по форме.
Частое обновление formState.errors может вызывать лишние
ререндеры. Оптимизация достигается через:
useFormStateconst { errors } = useFormState({
control,
name: "email"
});
Такой подход ограничивает перерендер только теми компонентами, которые зависят от конкретного поля.
В многоязычных интерфейсах Yup может использоваться с функцией трансляции:
yup.setLocale({
required: "Обязательное поле",
email: "Неверный формат email"
});
Это централизует контроль текстов и исключает необходимость локализации на уровне UI.
При этом UI остаётся нейтральным:
<span>{errors.email?.message}</span>
При вводе нового значения ошибка может сохраняться до повторной валидации. Типичный сценарий:
Решение заключается в стратегии режима валидации:
onChangeonBluronSubmituseForm({
resolver: yupResolver(schema),
mode: "onChange"
});
Выбор режима напрямую влияет на то, когда интерфейс обновляет состояние ошибок.
В крупных интерфейсах применяется единый компонент:
const FieldError = ({ error }) =>
error ? <span className="error">{error.message}</span> : null;
Это обеспечивает консистентность отображения независимо от типа поля, формы или источника данных.
Ошибки, возвращаемые YupResolver, формируют связующее
звено между схемой валидации и визуальным состоянием интерфейса, где
ключевым становится не сама проверка, а предсказуемость отображения
результата на каждом уровне структуры формы.