Валидационные схемы в Yup строятся как декларативное описание правил
для отдельных полей, однако в реальных формах часто возникает
необходимость проверять взаимосвязи между значениями разных полей. Для
этого используется механизм ссылок (refs), позволяющий
одному полю обращаться к значению другого внутри одной схемы.
yup.refФункция yup.ref() создаёт ссылку на другое поле в
пределах текущего объекта схемы. Это не прямое обращение к значению в
момент объявления, а динамическая привязка, которая разрешается во время
валидации.
Ключевая особенность заключается в том, что Yup не «считывает» значение при создании схемы — ссылка сохраняется как описание зависимости.
Простейший пример:
import * as yup fr om 'yup';
const schema = yup.object({
password: yup.string().required(),
confirmPassword: yup
.string()
.oneOf([yup.ref('password')], 'Пароли не совпадают')
.required()
});
Здесь confirmPassword сравнивается с
password через yup.ref('password'). При
изменении значения password в форме результат валидации
автоматически изменится без изменения самой схемы.
refs
в контексте объектаСсылки работают только внутри одного уровня объекта схемы. Это означает, что Yup ожидает структуру данных, соответствующую описанию схемы.
const schema = yup.object({
user: yup.object({
password: yup.string().required(),
confirm: yup
.string()
.oneOf([yup.ref('user.password')], 'Ошибка совпадения')
})
});
Здесь используется путь user.password, который
интерпретируется как доступ к вложенному значению через точечную
нотацию.
ref в числовых сравненияхОдна из распространённых задач — сравнение чисел между собой: минимальное и максимальное значение, диапазоны, ограничения на количество.
const schema = yup.object({
min: yup.number().required(),
max: yup
.number()
.required()
.min(yup.ref('min'), 'max должен быть больше min')
});
yup.ref('min') подставляет текущее значение поля
min в момент проверки поля max.
Важно, что порядок объявления полей не влияет на корректность работы — Yup разрешает ссылки во время валидации всего объекта.
when и refsrefs часто используются вместе с методом
when, который позволяет строить условную валидацию на
основе других полей.
const schema = yup.object({
type: yup.string().required(),
value: yup.string().when('type', {
is: 'email',
then: schema => schema.email(),
otherwise: schema => schema.min(3)
})
});
Здесь type управляет логикой валидации
value. Хотя when может работать и без
ref, внутри него часто используются ссылки для более
сложных условий.
this.resolveВнутри кастомных тестов Yup предоставляет доступ к контексту, где
ref может быть разрешён вручную.
const schema = yup.object({
start: yup.date(),
end: yup.date().test('check-range', function (value) {
const start = this.resolve(yup.ref('start'));
if (!start || !value) return true;
return value >= start;
})
});
Метод this.resolve позволяет получить актуальное
значение поля, на которое указывает ref, в момент
выполнения проверки.
refs в
testКастомные проверки являются одной из самых гибких точек применения ссылок.
const schema = yup.object({
password: yup.string(),
confirm: yup.string().test(
'match',
'Пароли не совпадают',
function (value) {
return value === this.resolve(yup.ref('password'));
}
)
});
Такой подход позволяет реализовывать любую логику сравнения,
выходящую за пределы стандартных методов вроде oneOf.
В массивах ссылки также применимы, но требуют понимания структуры данных.
const schema = yup.object({
items: yup.array().of(
yup.object({
min: yup.number(),
max: yup.number().min(yup.ref('min'))
})
)
});
Внутри of() ссылка min относится к текущему
объекту элемента массива, а не к внешнему уровню.
refYup поддерживает как относительные, так и абсолютные пути к полям.
field — текущий уровень объектаparent.field — родительский уровеньnested.field — вложенная структураyup.ref('settings.lim it')
yup.ref('$contextValue')
Также возможны ссылки на контекст, передаваемый при валидации:
schema.validate(data, { context: { role: 'admin' } });
И доступ:
yup.ref('$role')
Один и тот же ref может использоваться в нескольких
местах схемы, что позволяет централизовать логику зависимостей.
const passwordRef = yup.ref('password');
const schema = yup.object({
password: yup.string(),
confirm: yup.string().oneOf([passwordRef]),
oldPassword: yup.string().notOneOf([passwordRef])
});
Это уменьшает дублирование и упрощает поддержку сложных схем.
refs разрешаются в момент валидации, а не в момент
создания схемы. Это означает:
Однако есть важное ограничение: ссылки не работают вне контекста
Yup-объекта. Попытка использовать ref как обычную
переменную приведёт к отсутствию значения.
В реальных формах часто возникает цепочка зависимостей:
const schema = yup.object({
minAge: yup.number(),
maxAge: yup
.number()
.min(yup.ref('minAge'))
.when('minAge', (minAge, schema) => {
if (minAge > 18) {
return schema.max(100);
}
return schema.max(50);
})
});
Здесь одновременно используются ref и when,
создавая многоуровневую зависимость между значениями.
refsОдной из частых проблем является попытка использовать несуществующий путь:
yup.ref('user.password') // если user отсутствует в структуре
В этом случае Yup не выбрасывает ошибку сразу, но значение становится
undefined, что приводит к некорректной валидации.
Другой распространённый случай — ожидание синхронного поведения в
асинхронной валидации. refs не ускоряют и не замедляют
проверку, они лишь подставляют актуальные значения.
refs с преобразованием данныхИногда требуется сравнивать нормализованные значения:
yup.string().transform((value) => value.trim())
.oneOf([yup.ref('otherField')])
Важно учитывать, что ref работает уже после
трансформации значений, поэтому сравнение происходит на итоговых
данных.
При сложных структурах данных ссылки могут становиться длинными:
yup.ref('settings.notifications.email.enabled')
Такие пути следует использовать осторожно, поскольку они увеличивают связанность схемы с конкретной структурой данных.
refs при частичной валидацииПри валидации отдельных полей (validateAt) ссылки всё
равно разрешаются в рамках всего объекта. Это означает, что Yup требует
доступ к полной структуре данных, даже если проверяется одно поле.
schema.validateAt('confirmPassword', data)
Если password отсутствует в data, результат
ссылки будет undefined.