Интеграция с нативными input элементами

HTML-инпуты формируют базовый слой пользовательского ввода, вокруг которого строится вся модель состояния формы в связке с React Hook Form и резолвером схемы валидации Yup. Основная идея интеграции заключается в том, что нативные элементы DOM остаются источником истины для пользовательского ввода, а библиотека управления формой лишь подписывается на их изменения через ref и события.

Механизм регистрации нативных элементов через register

В React Hook Form нативные input-элементы подключаются через register, который создаёт мост между DOM и системой валидации.

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

Регистрация обеспечивает:

  • отслеживание value без controlled state
  • минимальное количество перерисовок
  • привязку к schema validation через YupResolver

Пример базовой регистрации:

const { register, handleSubmit } = useForm({
  resolver: yupResolver(schema),
});

<input type="text" {...register("username")} />

Нативный input остаётся uncontrolled, а его состояние извлекается через ref.

Связка YupResolver с DOM-структурой формы

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

  1. Сбор значений из зарегистрированных input
  2. Передача данных в Yup schema
  3. Получение результата validationResult
  4. Преобразование ошибок в структуру React Hook Form
const schema = yup.object({
  username: yup.string().required(),
});

Ключевой момент заключается в том, что нативный input не знает о правилах валидации, вся логика изолирована в resolver.

Особенности работы с различными типами input

text и number

Нативный input type="text" возвращает строку независимо от ожидаемого типа. При использовании YupResolver часто возникает необходимость преобразования типов.

age: yup.number().transform((value) => Number(value))

Важно учитывать, что DOM всегда возвращает string.


checkbox

Checkbox имеет особую модель данных: checked вместо value.

<input type="checkbox" {...register("isActive")} />

Yup-схема:

isActive: yup.boolean()

React Hook Form автоматически маппит checked в boolean.


radio group

Radio buttons группируются по имени поля, а значение определяется выбранным input.

<input type="radio" value="male" {...register("gender")} />
<input type="radio" value="female" {...register("gender")} />

Yup:

gender: yup.string().oneOf(["male", "female"])

select

Select элемент возвращает строковое значение выбранного option.

<select {...register("country")}>
  <option value="kz">Kazakhstan</option>
  <option value="ru">Russia</option>
</select>

При необходимости преобразования типов используется transform в Yup.


file input

File input не сериализуется в строку и требует отдельной обработки:

<input type="file" {...register("avatar")} />

Yup-схема:

avatar: yup
  .mixed()
  .test("fileSize", "File too large", (file) => {
    return file && file[0]?.size < 2000000;
  });

Контроль значений и defaultValues

Нативные input элементы опираются на defaultValue и defaultChecked при инициализации.

useForm({
  defaultValues: {
    username: "",
    isActive: false,
  },
});

При использовании YupResolver важно, чтобы defaultValues соответствовали типам схемы, иначе возможны расхождения между initial state и validation schema.

Синхронизация ошибок с нативными элементами

Ошибки, возвращаемые YupResolver, привязываются к конкретным полям через formState.errors.

const {
  register,
  formState: { errors },
} = useForm({
  resolver: yupResolver(schema),
});

Отображение ошибки рядом с input:

<input {...register("username")} />
<p>{errors.username?.message}</p>

Каждый нативный input остаётся неизменным, а ошибки накладываются поверх DOM-структуры.

Поведение uncontrolled inputs в React Hook Form

Нативные input работают в режиме uncontrolled, что даёт:

  • отсутствие локального state в React
  • снижение количества ререндеров
  • прямую работу с DOM

Изменения фиксируются через event listeners, а не через onChange state-binding.

Особенности интеграции с nested fields

Нативные input поддерживают вложенные структуры через dot notation:

<input {...register("profile.firstName")} />
<input {...register("profile.lastName")} />

Yup schema:

profile: yup.object({
  firstName: yup.string().required(),
  lastName: yup.string().required(),
});

React Hook Form автоматически формирует вложенный объект из плоских DOM значений.

Работа с массивами input элементов

Массивы реализуются через индексированные имена:

<input {...register("items.0.name")} />
<input {...register("items.1.name")} />

Yup:

items: yup.array().of(
  yup.object({
    name: yup.string().required(),
  })
);

Нативные input не знают о структуре массива — она формируется на уровне формы.

Влияние YupResolver на жизненный цикл input

При каждом submit происходит:

  • чтение DOM значений
  • построение объекта формы
  • запуск Yup validation
  • возврат ошибок в React Hook Form

Это означает, что нативные input не участвуют в валидации напрямую и остаются изолированными от бизнес-логики.

Оптимизация взаимодействия с DOM

Использование нативных input в связке с React Hook Form и YupResolver снижает количество операций:

  • отсутствует двойной binding
  • минимизированы re-render cycles
  • исключены controlled state updates

Особенно заметно это при большом количестве полей (50+), где controlled inputs приводят к деградации производительности.

Особенности event-based обновлений

Нативные input работают через стандартные DOM события:

  • input
  • change
  • blur

React Hook Form подписывается на них через ref, а YupResolver срабатывает только при необходимости, а не на каждый keystroke, если не включён режим mode: "onChange".

Типовые ошибки интеграции

Расхождение типов между DOM и Yup schema:

  • string vs number
  • missing boolean conversion
  • undefined vs empty string

Некорректная инициализация defaultValues:

  • uncontrolled to controlled warning при смене режима
  • mismatch между schema и initial state

Неправильная обработка file input:

  • попытка сериализовать FileList как string
  • отсутствие проверки массива файлов

Итоговая модель взаимодействия

Нативные input элементы формируют слой ввода, React Hook Form управляет регистрацией и жизненным циклом данных, а YupResolver выполняет централизованную валидацию без вмешательства в DOM. Такая архитектура разделяет ответственность: HTML отвечает за ввод, форма за состояние, схема за правила данных.