В Yup схемы валидации не обязаны быть статичными структурами, заранее описанными один раз и навсегда. В реальных приложениях форма часто меняется в зависимости от состояния данных, роли пользователя, ответа сервера или выбранных опций интерфейса. Именно для таких случаев используются динамические схемы, позволяющие строить и изменять правила валидации на основе входных параметров.
Динамическая схема в Yup — это способ создавать или модифицировать структуру валидации во время выполнения программы. Вместо фиксированного объекта с правилами используется функция, которая возвращает схему в зависимости от входных данных.
Ключевая особенность заключается в том, что схема становится функцией от состояния:
import * as Yup from 'yup';
const createSchema = (config) => {
return Yup.object().shape({
username: Yup.string().required('Обязательное поле'),
email: config.requireEmail
? Yup.string().email('Некорректный email').required('Email обязателен')
: Yup.string().email('Некорректный email').notRequired(),
});
};
Здесь структура валидации зависит от
config.requireEmail, что позволяет гибко управлять
поведением формы.
Yup поддерживает механизм context, который позволяет
передавать внешние параметры в схему без пересоздания
функции-генератора.
const schema = Yup.object().shape({
password: Yup.string().required(),
confirmPassword: Yup.string().oneOf(
[Yup.ref('password')],
'Пароли должны совпадать'
),
});
schema.validate(data, {
context: { minPasswordStrength: 3 }
});
Хотя context чаще используется в кастомных проверках, он
становится основой для построения более сложной логики валидации.
Одним из ключевых инструментов динамических схем является метод
when. Он позволяет изменять правила поля в зависимости от
значения другого поля.
const schema = Yup.object().shape({
hasPhone: Yup.boolean(),
phone: Yup.string().when('hasPhone', {
is: true,
then: (schema) => schema.required('Укажите номер телефона'),
otherwise: (schema) => schema.notRequired(),
}),
});
Здесь поле phone становится обязательным только при
включённом флаге hasPhone.
Можно усложнять условия, используя несколько зависимостей:
const schema = Yup.object().shape({
role: Yup.string(),
accessCode: Yup.string().when('role', {
is: (val) => val === 'admin' || val === 'moderator',
then: (schema) => schema.required('Код доступа обязателен'),
otherwise: (schema) => schema.strip(),
}),
});
Метод strip() удаляет поле из результата валидации, что
полезно при работе с условными формами.
В сложных формах структура объекта может зависеть от внешних данных: списка полей с сервера, конфигурации UI или пользовательских настроек.
const buildSchemaFromFields = (fields) => {
const shape = {};
fields.forEach((field) => {
if (field.type === 'text') {
shape[field.name] = Yup.string();
}
if (field.required) {
shape[field.name] = shape[field.name].required('Обязательное поле');
}
if (field.type === 'number') {
shape[field.name] = Yup.number();
}
});
return Yup.object().shape(shape);
};
Такой подход позволяет строить форму полностью на основе конфигурации, не меняя код схемы при добавлении новых полей.
Метод lazy позволяет создавать схему только в момент
валидации значения. Это особенно полезно, когда структура данных заранее
неизвестна.
const schema = Yup.lazy((value) => {
if (typeof value === 'string') {
return Yup.string().min(3);
}
if (typeof value === 'number') {
return Yup.number().positive();
}
return Yup.mixed();
});
Такой механизм часто используется при работе с полиморфными структурами или API, возвращающим неоднородные данные.
Yup позволяет строить динамические схемы и для массивов, включая вложенные объекты.
const schema = Yup.object().shape({
users: Yup.array().of(
Yup.object().shape({
name: Yup.string().required(),
age: Yup.number().min(18),
})
),
});
Если структура элементов массива зависит от внешних условий, можно
комбинировать when и lazy:
const userSchema = Yup.lazy((value) => {
if (value?.type === 'admin') {
return Yup.object({
permissions: Yup.array().of(Yup.string()).required(),
});
}
return Yup.object({
permissions: Yup.array().notRequired(),
});
});
Часто динамические схемы оформляют как фабрики, чтобы переиспользовать логику в разных частях приложения.
const createPasswordSchema = ({ minLength = 8 }) =>
Yup.string()
.min(minLength, `Минимум ${minLength} символов`)
.required('Пароль обязателен');
const schema = Yup.object().shape({
password: createPasswordSchema({ minLength: 12 }),
});
Такой подход снижает дублирование и упрощает поддержку сложных форм.
Часто правила зависят от данных, полученных с сервера: лимиты, настройки аккаунта, конфигурации продукта.
const createSchema = (settings) =>
Yup.object().shape({
age: Yup.number()
.min(settings.minAge)
.max(settings.maxAge)
.required(),
});
Если настройки обновляются динамически, схема может пересоздаваться при каждом изменении состояния приложения.
В реальных формах часто встречаются цепочки зависимостей между
несколькими полями. Yup позволяет описывать такие сценарии через
вложенные when.
const schema = Yup.object().shape({
country: Yup.string(),
region: Yup.string().when('country', {
is: 'Russia',
then: (schema) => schema.required(),
otherwise: (schema) => schema.notRequired(),
}),
city: Yup.string().when(['country', 'region'], {
is: (country, region) => country === 'Russia' && region === 'Moscow',
then: (schema) => schema.required('Укажите город'),
}),
});
Такая композиция позволяет строить поведение формы, близкое к бизнес-логике, а не к статической проверке данных.
В связке с библиотеками управления формами динамические схемы становятся основным инструментом адаптации интерфейса. Схема пересоздаётся при изменении состояния формы, пользовательского выбора или внешнего контекста.
Особенность заключается в том, что Yup не хранит состояние формы — он лишь описывает правила. Поэтому динамическая генерация схемы становится ответственностью слоя приложения.
При чрезмерном усложнении логики генерации схемы возникает риск
потери прозрачности валидации. Условные цепочки when,
вложенные lazy и фабрики могут затруднять отладку и
предсказуемость поведения.
Поэтому динамическая схема требует строгой структуры генераторов и разделения логики:
Такой подход сохраняет читаемость даже при сложной валидации и большом количестве зависимостей между полями.