Валидационный процесс в связке Yup + YupResolver в прикладных React-приложениях строится не только вокруг схемы, но и вокруг этапа подготовки данных. На практике именно предобработка определяет стабильность формы: одинаковые поля могут приходить в разных форматах, содержать «пустые» значения, строковые представления чисел или некорректные даты.
Перед передачей данных в yupResolver они проходят через
слой нормализации, который формируется либо вручную, либо через
возможности самой схемы Yup.
Типовой поток данных выглядит так:
raw input → нормализация → преобразование типов → схема Yup → результат валидации
Игнорирование первого этапа приводит к накоплению технического долга: схемы начинают разрастаться из-за попыток компенсировать грязные данные, а логика становится менее предсказуемой.
YupResolver выступает связующим звеном между формой
(например, react-hook-form) и схемой Yup. Его задача
заключается не только в запуске валидации, но и в подготовке данных к
корректной проверке.
В базовом виде resolver:
schema.validateВажно учитывать, что YupResolver не заменяет полноценную предобработку. Он лишь обеспечивает корректный запуск схемы и возвращение результата в формат, понятный форме.
Наиболее частая проблема — строки с неочевидной семантикой:
" " (пробелы)"" (пустая строка)"null" или "undefined" как текстВ рамках схемы Yup это решается через transform:
import * as yup from "yup";
const schema = yup.object({
username: yup
.string()
.transform((value) => {
if (typeof value !== "string") return value;
const trimmed = value.trim();
return trimmed === "" ? null : trimmed;
})
.nullable()
});
Такой подход позволяет:
requiredHTML-формы всегда возвращают строки, даже если поле выглядит как число. Это фундаментальная особенность DOM-форм.
Предобработка числовых значений часто реализуется через
transform:
age: yup
.number()
.transform((value, originalValue) => {
if (originalValue === "") return null;
const parsed = Number(originalValue);
return Number.isNaN(parsed) ? undefined : parsed;
})
.nullable()
Ключевая цель — отделить:
Без этого разделения Yup начинает смешивать ошибки типов и обязательности поля.
Дата — один из наиболее нестабильных типов данных в веб-формах.
Источники проблем:
"YYYY-MM-DD"Пример предобработки:
birthDate: yup
.date()
.transform((value, originalValue) => {
if (!originalValue) return null;
const date = new Date(originalValue);
return isNaN(date.getTime()) ? undefined : date;
})
.nullable()
Такой подход позволяет схеме работать с уже нормализованным
Date, а не с сырыми строками.
Yup предоставляет встроенный механизм transform, который
фактически заменяет отдельный preprocessing слой.
Основные особенности:
undefined, что трактуется как
invalidПример комбинированной логики:
const schema = yup.object({
price: yup
.number()
.transform((value, originalValue) => {
if (typeof originalValue === "string") {
const cleaned = originalValue.replace(",", ".");
const parsed = parseFloat(cleaned);
return isNaN(parsed) ? undefined : parsed;
}
return value;
})
});
Хотя YupResolver сам по себе не является инструментом трансформации данных, он влияет на поведение предобработки через параметры и стратегию выполнения схемы.
Один из ключевых механизмов — удаление лишних полей:
import { yupResolver } from "@hookform/resolvers/yup";
const resolver = yupResolver(schema, {
stripUnknown: true
});
Эффект:
Параметр влияет на стратегию сбора ошибок:
yupResolver(schema, {
abortEarly: false
});
Поведение:
true — остановка на первой ошибкеfalse — сбор всех ошибокХотя параметр не относится напрямую к предобработке, он влияет на стратегию анализа данных после неё.
Под «превью» в контексте YupResolver обычно понимается промежуточное состояние данных перед финальной валидацией. Это важно для:
Поскольку YupResolver не предоставляет встроенный hook для preview,
используется комбинация схемы и ручного вызова cast или
validate.
const previewData = schema.cast(formValues, {
stripUnknown: true
});
Метод cast позволяет увидеть:
Ключевое различие:
cast — только преобразованиеvalidate — преобразование + проверка правилconst casted = schema.cast(values);
const validated = await schema.validate(values);
cast часто используется именно для превью слоя
предобработки, поскольку не выбрасывает ошибки.
В связке с react-hook-form можно формировать «промежуточный слой анализа»:
const onCha nge = (values) => {
const preview = schema.cast(values);
console.log("PREVIEW:", preview);
};
Такой подход позволяет:
Одна из ключевых проблем валидационных схем — недетерминированность поведения при разных входных данных.
YupResolver усиливает требования к детерминированности:
Для этого важно избегать:
В сложных формах предобработка редко ограничивается одним transform. Обычно она распределяется по слоям:
Такое разделение уменьшает связность и упрощает поддержку.
Массивы требуют отдельного подхода, так как предобработка должна применяться рекурсивно.
const schema = yup.object({
tags: yup
.array()
.of(
yup
.string()
.transform((v) => (v?.trim() === "" ? null : v))
)
});
Особенность:
При больших формах предобработка становится значимым фактором производительности.
Проблемные сценарии:
Оптимизационные подходы:
Некорректная предобработка часто проявляется не как ошибка схемы, а как «тихая деградация»:
undefined вместо ожидаемого
значенияТипичный анти-паттерн:
.transform((v) => Number(v))
Без проверки это приводит к:
Корректный вариант требует явной обработки:
.transform((v) => {
const n = Number(v);
return Number.isNaN(n) ? undefined : n;
});
Одним из принципов стабильной работы YupResolver является идентичность поведения:
Если эти два слоя дают разные результаты, это сигнал о нарушении целостности схемы.
Причины расхождений:
.whenСтабильная схема должна обеспечивать одинаковую интерпретацию данных на обоих этапах.