Установка и первоначальная настройка

Работа с YupResolver в экосистеме React Hook Form строится вокруг нескольких ключевых пакетов: react-hook-form, @hookform/resolvers и yup. Каждый из них выполняет строго определённую роль: управление состоянием формы, адаптация валидаторов и декларативное описание схем валидации соответственно.

Установка через npm:

npm install react-hook-form yup @hookform/resolvers

Аналогичная установка через yarn:

yarn add react-hook-form yup @hookform/resolvers

При использовании pnpm:

pnpm add react-hook-form yup @hookform/resolvers

Версионная совместимость имеет значение, особенно в контексте TypeScript-проектов. @hookform/resolvers синхронизируется с версиями React Hook Form, поэтому при обновлении одной части стека требуется проверка остальных зависимостей.


Архитектурная роль YupResolver

YupResolver выступает связующим адаптером между декларативной схемой валидации Yup и механизмом валидации React Hook Form.

Основная задача заключается в преобразовании схемы Yup в формат, понятный системе валидации формы. Это позволяет:

  • централизовать правила валидации;
  • использовать единый источник истины для формы;
  • отделить UI-логику от бизнес-правил;
  • обеспечить повторное использование схем.

Механизм работы основан на функции-обёртке, возвращающей асинхронный резолвер:

import { yupResolver } from '@hookform/resolvers/yup';

Базовая настройка Yup-схемы

Перед подключением резолвера формируется схема валидации с использованием Yup.

Простейший пример описания структуры данных:

import * as yup from 'yup';

const schema = yup.object({
  username: yup.string().required('Поле обязательно'),
  email: yup.string().email('Некорректный email').required('Обязательное поле'),
  age: yup.number().min(18, 'Минимальный возраст 18 лет')
});

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


Подключение YupResolver к React Hook Form

Интеграция осуществляется через хук useForm, где резолвер передаётся в конфигурацию формы.

import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';

Далее выполняется связывание схемы и формы:

const schema = yup.object({
  username: yup.string().required(),
  email: yup.string().email().required()
});

const form = useForm({
  resolver: yupResolver(schema)
});

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


Механизм выполнения валидации

При каждом изменении состояния формы или при попытке отправки данных React Hook Form передаёт текущие значения в yupResolver.

Процесс включает несколько этапов:

  1. Сбор текущих значений формы.
  2. Передача данных в Yup-схему.
  3. Выполнение синхронной или асинхронной проверки.
  4. Формирование объекта ошибок в стандартизированном формате.
  5. Возврат результата обратно в React Hook Form.

Ошибки преобразуются в структуру:

{
  fieldName: {
    type: "validation-type",
    message: "Описание ошибки"
  }
}

Типизация с TypeScript

При использовании TypeScript важным аспектом становится синхронизация типов между схемой и формой.

Yup предоставляет механизм вывода типов через InferType:

import * as yup from 'yup';

const schema = yup.object({
  username: yup.string().required(),
  email: yup.string().email().required()
});

type FormData = yup.InferType<typeof schema>;

Далее тип передаётся в useForm:

const form = useForm<FormData>({
  resolver: yupResolver(schema)
});

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


Асинхронная валидация и внешние проверки

Yup поддерживает асинхронные проверки через метод test, что позволяет интегрировать серверные проверки или обращения к внешним источникам.

Пример проверки уникальности:

const schema = yup.object({
  username: yup.string().test(
    'check-username',
    'Имя уже занято',
    async (value) => {
      const response = await fetch(`/api/check-username?value=${value}`);
      const data = await response.json();
      return data.available;
    }
  )
});

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


Обработка режимов валидации

React Hook Form поддерживает различные режимы триггера валидации:

  • onSubmit — проверка при отправке формы;
  • onChange — проверка при каждом изменении поля;
  • onBlur — проверка при потере фокуса;
  • all — комбинация поведения.

Пример конфигурации:

const form = useForm({
  resolver: yupResolver(schema),
  mode: 'onChange'
});

Выбор режима влияет на количество вызовов YupResolver и, соответственно, на производительность валидации.


Передача контекста в схему

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

Пример использования:

const schema = yup.object({
  password: yup.string().required(),
  confirmPassword: yup.string().oneOf(
    [yup.ref('password')],
    'Пароли не совпадают'
  )
});

Дополнительно возможно использование context в резолвере:

useForm({
  resolver: yupResolver(schema, { context: { role: 'admin' } })
});

Внутри схемы контекст доступен через this.options.context.


Кастомизация обработки ошибок

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

Некоторые сценарии требуют нормализации сообщений или изменения структуры вложенности. В таких случаях применяется промежуточная обработка результатов:

const resolver = yupResolver(schema, {
  abortEarly: false
});

Опция abortEarly: false позволяет собрать все ошибки сразу, а не останавливаться на первой найденной.


Поведение при вложенных объектах и массивах

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

Пример:

const schema = yup.object({
  user: yup.object({
    name: yup.string().required(),
    contacts: yup.array().of(
      yup.object({
        type: yup.string().required(),
        value: yup.string().required()
      })
    )
  })
});

YupResolver преобразует вложенные ошибки в формат, совместимый с React Hook Form, сохраняя путь к каждому полю.


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

При работе с большими формами значительное влияние оказывает частота вызовов резолвера и сложность схемы.

Основные факторы оптимизации:

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

Пример мемоизации схемы:

const schema = useMemo(() => yup.object({
  username: yup.string().required(),
  email: yup.string().email().required()
}), []);

Это предотвращает пересоздание схемы при каждом рендере компонента.


Интеграция с динамическими формами

Динамические формы, где количество полей изменяется во время выполнения, требуют корректного описания массивов в Yup.

Пример:

const schema = yup.object({
  fields: yup.array().of(
    yup.object({
      value: yup.string().required()
    })
  )
});

React Hook Form синхронизирует изменения структуры формы с результатами YupResolver, обеспечивая корректную валидацию даже при добавлении или удалении элементов массива.


Поведение при сбросе формы

При вызове reset React Hook Form сбрасывает состояние, однако YupResolver не выполняет дополнительной логики, так как валидация запускается только при триггере событий формы.

form.reset({
  username: '',
  email: ''
});

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