Массивы объектов

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

Структура массива объектов в Yup

Базовый подход к описанию массива объектов строится вокруг комбинации array() и object():

  • массив задаёт контейнер данных
  • объект описывает структуру каждого элемента

Типовая схема принимает следующий вид:

import * as Yup from "yup";

const schema = Yup.object({
  users: Yup.array()
    .of(
      Yup.object({
        id: Yup.number().required(),
        name: Yup.string().required(),
        email: Yup.string().email().required()
      })
    )
});

Каждый элемент массива становится независимой сущностью валидации, но при этом сохраняет связь с родительским полем users.

Вложенные ошибки и их структура

При работе с массивами объектов формируется иерархическая структура ошибок:

  • users — ошибка уровня массива
  • users[0] — ошибка конкретного элемента
  • users[0].email — ошибка конкретного поля объекта

Такой формат позволяет точно локализовать проблему, однако требует корректного преобразования в UI-слое, поскольку плоская модель ошибок не отражает вложенность.

Пример результата ошибки:

{
  users: [
    null,
    { email: "Некорректный email" },
    null
  ]
}

или в формате path-based структуры:

{
  "users[1].email": "Некорректный email"
}

Разные реализации резолверов могут возвращать различные форматы, но YupResolver приводит их к форме, совместимой с React Hook Form.

Использование с динамическими формами

Массивы объектов часто применяются в динамических формах, где количество элементов изменяется во время выполнения. Типичный сценарий — список пользователей, товаров, адресов.

const defaultValues = {
  users: [
    { id: 1, name: "", email: "" }
  ]
};

При использовании useFieldArray структура синхронизируется с Yup-схемой:

  • добавление элемента требует соответствия объектной схеме
  • удаление элемента автоматически корректирует индексы
  • обновление поля сохраняет привязку к пути валидации

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

Валидация вложенных объектов внутри массива

Массивы объектов часто содержат дополнительные уровни вложенности. Например:

const schema = Yup.object({
  orders: Yup.array().of(
    Yup.object({
      id: Yup.number().required(),
      items: Yup.array().of(
        Yup.object({
          productId: Yup.number().required(),
          quantity: Yup.number().min(1).required()
        })
      )
    })
  )
});

Здесь формируется трёхуровневая структура:

  • orders
  • orders[i]
  • orders[i].items[j]

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

Поведение required и пустых массивов

Пустой массив в Yup по умолчанию считается валидным, если явно не задано ограничение:

Yup.array().min(1, "Должен быть хотя бы один элемент")

Дополнительные ограничения:

  • min(n) — минимальное количество элементов
  • max(n) — максимальное количество элементов
  • required() — обязательное наличие массива

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

Преобразование данных перед валидацией

Массивы объектов часто требуют нормализации перед проверкой:

  • приведение строковых чисел к number
  • удаление пустых объектов
  • фильтрация null-значений

Yup поддерживает трансформации:

Yup.array().transform((value) => value.filter(Boolean))

Такие преобразования влияют на итоговую структуру данных до применения схемы объекта.

Взаимосвязь с резолвером формы

При интеграции с React Hook Form схема Yup не используется напрямую. Вместо этого применяется резолвер, который:

  1. получает значения формы
  2. запускает Yup-схему
  3. преобразует ошибки в формат RHF
  4. возвращает результат валидации

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

Оптимизация валидации массивов

При больших массивах объектов возникают проблемы производительности:

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

Практика оптимизации включает:

  • мемоизацию схемы
  • разделение массивов на логические группы
  • использование validateAt для точечной проверки

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

Условная валидация внутри массива

Элементы массива часто зависят друг от друга. Yup поддерживает условные правила через when:

Yup.object({
  type: Yup.string(),
  value: Yup.string().when("type", {
    is: "email",
    then: (schema) => schema.email(),
    otherwise: (schema) => schema.min(3)
  })
});

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

Ошибки валидации и их нормализация

Результаты валидации массивов объектов часто требуют дополнительной обработки:

  • преобразование вложенных путей в map-структуры
  • приведение индексов к строковым ключам
  • группировка ошибок по уровням вложенности

Типичный формат нормализации:

{
  0: { email: "Ошибка" },
  2: { name: "Обязательное поле" }
}

Такой подход упрощает отображение ошибок в компонентах, работающих с индексами массива.

Частичные обновления и re-validation

При изменении массива важно учитывать стратегию повторной валидации:

  • при добавлении элемента проверяется только новый объект
  • при изменении поля проверяется конкретный путь
  • при удалении элемента пересчитываются индексы

YupResolver обеспечивает согласованность между структурой Yup и состоянием формы, но логика частичной валидации зависит от конфигурации формы и используемого режима проверки (mode, reValidateMode).

Работа с дефолтными значениями

Массивы объектов требуют строгого соответствия между defaultValues и схемой:

  • отсутствие поля приводит к undefined
  • несовпадение структуры вызывает ошибки привязки
  • пустые массивы могут трактоваться как валидные или невалидные в зависимости от схемы

Корректная инициализация снижает количество ошибок синхронизации между UI и схемой.

Глубокая типизация структуры массива

При использовании TypeScript массивы объектов становятся источником сложных типов:

  • Array<InferType<typeof schema>>
  • вложенные интерфейсы для каждого уровня
  • строгая типизация индексов

Это позволяет предотвратить ошибки ещё до этапа выполнения, особенно при динамическом добавлении элементов.

Комбинирование массивов и других структур

Массивы объектов часто комбинируются с:

  • объектами верхнего уровня
  • вложенными массивами
  • условными схемами

Пример сложной структуры:

Yup.object({
  project: Yup.string(),
  teams: Yup.array().of(
    Yup.object({
      name: Yup.string(),
      members: Yup.array().of(
        Yup.object({
          username: Yup.string(),
          role: Yup.string()
        })
      )
    })
  )
});

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

Поведение при асинхронной валидации

Асинхронные проверки (например, проверка уникальности email) внутри массивов выполняются для каждого элемента независимо:

  • параллельные запросы
  • возможные гонки состояний
  • необходимость кеширования результатов

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

Управление изменяемыми индексами

При удалении элементов массива индексы пересчитываются, что влияет на:

  • привязку ошибок
  • состояние touched/dirty
  • синхронизацию с UI

Корректная обработка требует пересчёта путей всех последующих элементов, иначе возможны «смещённые» ошибки.

Особенности поведения при частично заполненных объектах

Массив объектов допускает частичное заполнение до момента финальной валидации:

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

Это поведение особенно важно при пошаговом заполнении форм, где элементы массива создаются постепенно.