React Hook Form

Управление формами в React традиционно связано с балансом между контролируемыми и неконтролируемыми компонентами. Библиотека React Hook Form строится вокруг идеи минимизации количества перерендеров и делегирования большей части работы нативным возможностям DOM.

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

Основные концепции

Неконтролируемые компоненты

React Hook Form по умолчанию опирается на неконтролируемые компоненты. Это означает, что:

  • состояние поля хранится в самом DOM
  • React не обновляется при каждом вводе
  • данные извлекаются через ref

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

Регистрация полей

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

import { useForm } from "react-hook-form";

const Form = () => {
  const { register, handleSubmit } = useForm();

  const onSub mit = (data) => {
    console.log(data);
  };

  return (
    <form onSub mit={handleSubmit(onSubmit)}>
      <input {...register("username")} />
      <input type="password" {...register("password")} />
      <button type="submit">Отправить</button>
    </form>
  );
};

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


useForm как центральный механизм

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

Основные возвращаемые значения

  • register — привязка полей
  • handleSubmit — обработка отправки формы
  • watch — наблюдение за значениями
  • setValue — программное изменение значения
  • getValues — получение текущих данных
  • reset — сброс формы
  • control — объект для интеграции с контролируемыми компонентами
const {
  register,
  handleSubmit,
  watch,
  setValue,
  getValues,
  reset,
  control
} = useForm();

Обработка отправки формы

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

const onSub mit = (data) => {
  console.log(data);
};

<form onSub mit={handleSubmit(onSubmit)}>

Механизм работы:

  1. собираются все зарегистрированные поля
  2. выполняется валидация
  3. при успехе вызывается callback
  4. при ошибке передаётся объект ошибок

Валидация данных

React Hook Form поддерживает декларативную валидацию через параметры register.

Встроенные правила

<input
  {...register("email", {
    required: "Email обязателен",
    pattern: {
      value: /\S+@\S+\.\S+/,
      message: "Неверный формат email"
    }
  })}
/>

Типы валидации

  • required — обязательное поле
  • min / max — числовые ограничения
  • minLength / maxLength — длина строки
  • pattern — регулярные выражения
  • validate — пользовательская функция
register("username", {
  validate: (value) => value !== "admin" || "Недопустимое имя"
});

Объект ошибок

Ошибки доступны через formState.errors.

const {
  register,
  handleSubmit,
  formState: { errors }
} = useForm();

Использование:

{errors.email && <span>{errors.email.message}</span>}

Каждое поле содержит собственный объект ошибки, если правило валидации не прошло.


Наблюдение за значениями

Функция watch позволяет отслеживать изменения полей.

const password = watch("password");

Можно наблюдать:

  • одно поле
  • несколько полей
  • всю форму
const formValues = watch();

Программное управление значениями

setValue

Используется для установки значения поля вручную.

setValue("username", "Ivan");

getValues

Позволяет получить текущие значения без подписки.

const values = getValues();

Сброс формы

Метод reset возвращает форму в начальное состояние.

reset();

Можно задать новые значения:

reset({
  username: "default"
});

Контролируемые компоненты

Некоторые UI-библиотеки не работают с ref, поэтому используется Controller.

import { Controller } from "react-hook-form";

Пример интеграции

<Controller
  name="select"
  control={control}
  render={({ field }) => (
    <sel ect {...field}>
      <option value="a">A</option>
      <option value="b">B</option>
    </select>
  )}
/>

Controller связывает внешние компоненты с системой формы.


Производительность и архитектурные особенности

React Hook Form минимизирует перерендеры за счёт нескольких механизмов:

  • отсутствие глобального состояния формы в React
  • использование ref вместо state
  • локализация обновлений только для затронутых полей

Это особенно важно для:

  • больших форм (50+ полей)
  • динамических форм
  • форм с вложенными структурами

Работа с массивами полей

Для динамических списков используется useFieldArray.

import { useFieldArray } fr om "react-hook-form";

Пример

const { fields, append, remove } = useFieldArray({
  control,
  name: "users"
});
fields.map((field, index) => (
  <input {...register(`users.${index}.name`)} />
));

Операции:

  • append — добавление элемента
  • remove — удаление
  • move — перемещение
  • replace — замена массива

Типизация с TypeScript

React Hook Form хорошо интегрируется с TypeScript через generics.

type FormValues = {
  username: string;
  age: number;
};

const { register } = useForm<FormValues>();

Это обеспечивает:

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

Асинхронная валидация

Валидация может быть асинхронной:

register("username", {
  validate: async (value) => {
    const res = await fetch(`/check?name=${value}`);
    return res.ok || "Имя занято";
  }
});

Особенности:

  • поддержка промисов
  • ожидание результата перед submit
  • блокировка отправки при ошибке

Интеграция с внешними схемами валидации

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

Zod

import { zodResolver } from "@hookform/resolvers/zod";
const schema = z.object({
  username: z.string().min(3)
});

const { register } = useForm({
  resolver: zodResolver(schema)
});

Yup

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

Контекст формы

В крупных приложениях используется FormProvider.

const methods = useForm();

<FormProvider {...methods}>
  <ChildForm />
</FormProvider>

Внутри:

const { register } = useFormContext();

Это позволяет избегать prop drilling.


Оптимизация рендеринга

Основные техники:

  • использование memo для компонентов полей
  • разделение формы на подкомпоненты
  • избегание подписки на всю форму через watch
  • использование useFormState для локальных обновлений
const { errors } = useFormState({
  control,
  name: "email"
});

Управление фокусом и UI-логикой

Можно программно управлять фокусом:

setFocus("username");

Это используется для:

  • прокрутки к ошибкам
  • UX-улучшений
  • пошаговых форм

Событийная модель

React Hook Form работает через подписку на события DOM:

  • input
  • change
  • blur
  • submit

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


Сложные структуры данных

Поддерживаются вложенные объекты:

register("user.address.city");

И массивы:

register("items.0.name");

Форма автоматически формирует структуру объекта:

{
  user: {
    address: {
      city: ""
    }
  }
}