Контекст валидации

Контекст валидации в Yup позволяет передавать внешние данные в момент выполнения проверки схемы и использовать их внутри правил валидации, кастомных тестов и условных схем. Этот механизм делает возможным создание динамических схем, поведение которых зависит не только от входного значения, но и от окружения, в котором выполняется проверка.

В Yup контекст передаётся через параметры метода валидации. Основной способ — использование options.context при вызове validate или validateSync.

Контекст становится доступным внутри схемы и всех её внутренних тестов.

import * as Yup fr om 'yup';

const schema = Yup.object({
  email: Yup.string().required()
});

schema.validate(
  { email: 'test@example.com' },
  {
    context: {
      role: 'admin'
    }
  }
);

Переданный объект не изменяет схему, но становится частью вычислительного окружения.

Доступ к контексту в кастомных тестах

Внутри test функция Yup предоставляет доступ к контексту через this.options.context.

const schema = Yup.string().test(
  'check-role',
  'Недостаточно прав',
  function (value) {
    const { role } = this.options.context || {};

    if (role !== 'admin') {
      return false;
    }

    return true;
  }
);

Контекст в этом случае позволяет отделить бизнес-логику от схемы и управлять поведением извне.

Контекст и условная валидация

Одним из ключевых применений контекста является условная валидация через when. Хотя when чаще использует значения полей, он также может опираться на внешний контекст через функцию.

const schema = Yup.object({
  password: Yup.string().when([], {
    is: () => true,
    then: (s) => s.min(8),
  })
});

Более гибкий вариант:

const schema = Yup.string().when('$minLength', (minLength, schema) => {
  return schema.min(minLength || 0);
});

Здесь $minLength берётся из контекста, переданного при валидации.

schema.validate('abc', {
  context: {
    minLength: 5
  }
});

Символ $ является соглашением для обозначения контекстных переменных в Yup.

Передача контекста в глубинные уровни схемы

Контекст доступен не только в корневой схеме, но и во всех вложенных структурах: объектах, массивах и вложенных полях.

const schema = Yup.object({
  user: Yup.object({
    name: Yup.string().when('$isLocked', (isLocked, schema) => {
      return isLocked ? schema.required() : schema.notRequired();
    })
  })
});
schema.validate(
  { user: { name: '' } },
  { context: { isLocked: true } }
);

Контекст распространяется рекурсивно вниз по дереву схемы.

Использование контекста в кастомных тестах mixed.test

Более низкоуровневый доступ к контексту осуществляется через mixed().test, который является базовым механизмом Yup.

const schema = Yup.mixed().test(
  'feature-flag',
  'Функция отключена',
  function (value) {
    const { featureEnabled } = this.options.context || {};

    return featureEnabled === true;
  }
);

Такой подход используется при реализации флагов функций и динамических ограничений.

Контекст и ссылки на другие поля

Контекст может комбинироваться с ref, что позволяет создавать гибридные правила, где часть данных берётся из формы, а часть — извне.

const schema = Yup.object({
  role: Yup.string(),
  secret: Yup.string().when(['role', '$env'], (role, env, schema) => {
    if (role === 'admin' && env === 'production') {
      return schema.required();
    }
    return schema.notRequired();
  })
});

Здесь одновременно используются данные объекта и внешнего окружения.

Контекст в ленивых схемах (lazy)

В lazy контекст особенно полезен, когда структура данных зависит от внешних параметров.

const schema = Yup.lazy((value, options) => {
  const { type } = options.context || {};

  if (type === 'array') {
    return Yup.array().of(Yup.string());
  }

  return Yup.object({
    value: Yup.string()
  });
});

Такой подход используется при динамической типизации входных данных.

Практические сценарии использования контекста

Мультитенантные системы

Контекст позволяет учитывать параметры клиента или организации.

const schema = Yup.string().test(
  'tenant-lim it',
  'Превышен лимит',
  function (value) {
    const { maxLength } = this.options.context || {};
    return !maxLength || value.length <= maxLength;
  }
);

Фичефлаги

Yup.boolean().test(
  'feature-toggle',
  'Функция отключена',
  function (value) {
    return this.options.context?.features?.newUI === true;
  }
);

Локализация сообщений

Контекст может использоваться для динамической локализации ошибок.

const schema = Yup.string().required(function () {
  const locale = this.options.context?.locale;

  if (locale === 'ru') {
    return 'Поле обязательно';
  }

  return 'Field is required';
});

Особенности работы контекста

Контекст в Yup имеет несколько характерных особенностей:

  • он не реактивен и не изменяется автоматически;
  • передаётся только в момент вызова validate;
  • не сериализуется вместе со схемой;
  • доступен только внутри функций валидации и тестов;
  • должен явно прокидываться при каждом вызове проверки.

Ограничения и предсказуемость

Контекст делает схему более гибкой, но одновременно снижает её детерминированность. Одна и та же схема может вести себя по-разному в зависимости от переданных данных окружения.

Особенно важно учитывать:

  • отсутствие контекста приводит к undefined;
  • глубокие зависимости усложняют отладку;
  • чрезмерное использование контекста может скрывать бизнес-логику внутри схем.

По этой причине контекст обычно применяют для параметров окружения, флагов и конфигураций, но не для основной бизнес-валидации данных формы.