ref) и его поведениеВалидационная библиотека Yup активно использует механизм ссылок
(ref) для построения зависимых правил валидации.
ref позволяет одному полю схемы ссылаться на значение
другого поля, формируя динамические зависимости внутри объекта.
Типичный пример:
import * as Yup from "yup";
const schema = Yup.object({
password: Yup.string().required(),
confirmPassword: Yup.string()
.oneOf([Yup.ref("password")], "Пароли должны совпадать")
.required(),
});
Здесь confirmPassword зависит от значения
password. Однако именно такие зависимости часто становятся
источником скрытых проблем при усложнении схем.
Одной из ключевых особенностей ref является то, что он
не хранит значение напрямую. Вместо этого он разрешается в
момент выполнения валидации. Это означает:
validate.При работе с вложенными объектами проблема усиливается:
const schema = Yup.object({
user: Yup.object({
password: Yup.string().required(),
confirm: Yup.string().oneOf([Yup.ref("password")]),
}),
});
Здесь Yup.ref("password") не указывает на
user.password, а ищет поле на уровне текущего
объекта. Это частый источник ошибок.
refref всегда интерпретируется относительно текущего уровня
схемы.
Рассмотрим структуру:
{
user: {
password: "123",
confirm: "123"
}
}
Корректная ссылка:
Yup.ref("password")
Неверная попытка:
Yup.ref("user.password") // не работает так, как ожидается
В Yup точечная нотация в ref не всегда работает как
доступ к глубокой вложенности. В сложных структурах требуется либо
поднятие уровня схемы, либо использование кастомной логики через
test.
Одной из наиболее сложных ситуаций являются циклические зависимости между полями.
Пример:
const schema = Yup.object({
a: Yup.number().when("b", (b, schema) =>
b > 10 ? schema.min(5) : schema.max(5)
),
b: Yup.number().when("a", (a, schema) =>
a > 10 ? schema.min(5) : schema.max(5)
),
});
Здесь возникает логический цикл:
a зависит от bb зависит от aВ результате:
Yup не строит граф зависимостей заранее, поэтому такие конструкции приводят к непредсказуемому поведению.
when и
динамические зависимостиМетод when является основным инструментом для построения
зависимостей:
Yup.string().when("isRequired", (isRequired, schema) => {
return isRequired ? schema.required() : schema;
});
Проблемы возникают, когда:
undefined трактуется неоднозначно;Пример множественных зависимостей:
Yup.string().when(["isAdmin", "country"], (isAdmin, country, schema) => {
if (isAdmin && country === "KZ") {
return schema.required();
}
return schema;
});
Здесь сложность возрастает экспоненциально при добавлении новых условий, а диагностика ошибок становится затруднительной.
В связке с формами (например, React-экосистема) часто возникает проблема устаревших значений.
Схема может быть валидирована на основе старого состояния, если:
validate;В результате:
ref возвращает актуальное значение только при повторной
валидации;refПри работе с вложенными структурами часто возникает ошибка неправильного разрешения пути:
const schema = Yup.object({
settings: Yup.object({
min: Yup.number(),
max: Yup.number().min(Yup.ref("min")),
}),
});
Ожидаемое поведение — ссылка на settings.min, но
фактически ref("min") ищет min внутри текущего
уровня settings.
Корректный способ:
Yup.ref("settings.min")
Однако даже этот вариант может работать нестабильно в зависимости от контекста вызова.
В массивах проблема усложняется ещё сильнее:
const schema = Yup.object({
items: Yup.array().of(
Yup.object({
min: Yup.number(),
value: Yup.number().min(Yup.ref("min")),
})
),
});
Здесь ref("min") должен ссылаться на поле внутри
текущего элемента массива. Это работает только при корректной
локализации контекста элемента.
Проблемы возникают при:
transform;test;При использовании TypeScript в связке с Yup возникают дополнительные сложности:
ref возвращает any;Пример:
Yup.string().oneOf([Yup.ref("password")])
Компилятор не гарантирует, что password существует в
схеме, что увеличивает риск скрытых ошибок.
lazy и динамическими схемамиlazy позволяет строить схему на основе значения:
Yup.lazy((value) => {
if (typeof value === "string") {
return Yup.string().required();
}
return Yup.number();
});
При сочетании с ref появляются следующие сложности:
Схемы Yup часто переиспользуются, что приводит к неожиданным эффектам:
ref может сохранять ссылку на старую структуру;На практике наиболее распространены следующие проблемы:
ref без учёта уровня
вложенности;undefined в зависимых полях;when и ref в одной логике;ref как замену бизнес-логике.Когда ref не может быть разрешён:
undefined;Это приводит к ситуациям, когда:
С ростом количества полей схема начинает напоминать граф зависимостей, однако Yup не предоставляет инструментов для его визуализации или анализа.
Это приводит к:
Работа с ref и зависимостями в Yup характеризуется
сочетанием нескольких факторов:
В результате сложные схемы требуют осторожного проектирования и строгого контроля структуры данных, иначе зависимая валидация становится источником трудноуловимых дефектов.