Хотите заказать веб-сайт? Связаться с нами

Формы в React: Hook Form, Formik, Final Form

Работа с формами — одна из самых частых задач в интерфейсной разработке, а React не предлагает единого встроенного решения для неё. Именно поэтому вокруг форм выросла целая экосистема сторонних библиотек — от лёгких и производительных до максимально гибких и декларативных. В этом уроке разберём три самых популярных инструмента: React Hook Form, Formik и Final Form, — и сравним, чем они отличаются и когда какой выбрать.

Формы в React: Hook Form, Formik, Final Form

Почему работа с формами в React — отдельная задача

В классическом React-компоненте форма чаще всего строится на контролируемых input-элементах: значение каждого поля хранится в state, а любое изменение вызывает событие onChange и обновление этого state. Подход удобен тем, что данные формы всегда синхронизированы с состоянием компонента, но у него есть обратная сторона — каждое нажатие клавиши приводит к повторному рендеру всего компонента формы, а иногда и его дочерних элементов.

Когда полей становится больше пяти-семи, а к ним добавляется валидация, отображение ошибок и состояние отправки, ручной код быстро превращается в громоздкий набор useState и условных проверок. Чтобы не писать эту логику каждый раз заново, сообщество создало специализированные библиотеки, которые берут на себя хранение значений, валидацию, отслеживание изменённых полей и оптимизацию рендеров. Три самых востребованных решения — React Hook Form, Formik и Final Form — отличаются архитектурой, но решают одну и ту же задачу.

React Hook Form

React Hook Form — библиотека, построенная вокруг идеи неконтролируемых полей: вместо того чтобы хранить значение каждого input в state, она подписывается на DOM-элемент напрямую через ref и считывает значения только в момент отправки формы или валидации. Благодаря этому промежуточный ввод символов не вызывает лишних рендеров, а сама библиотека остаётся очень лёгкой.

Установка и базовый пример

Библиотека устанавливается одной командой, а работа с ней начинается с хука useForm, который возвращает набор функций и объектов для управления формой.

npm install react-hook-form
import { useForm } from "react-hook-form";
 
type FormValues = {
  name: string;
  email: string;
};
 
function LoginForm() {
  const { register, handleSubmit, formState: { errors } } = useForm<FormValues>();
 
  const onSubmit = (data: FormValues) => {
    console.log(data);
  };
 
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("name", { required: "Введите имя" })} />
      {errors.name && <span>{errors.name.message}</span>}
 
      <input {...register("email", { required: true })} />
 
      <button type="submit">Отправить</button>
    </form>
  );
}

Функция register связывает DOM-элемент input с внутренним состоянием формы, а handleSubmit оборачивает обработчик отправки, предварительно запуская валидацию и передавая в колбэк уже провалидированные данные.

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

Правила валидации задаются прямо в вызове register — вторым аргументом объекта. Для сложных полей, которые приходят из сторонних UI-библиотек (например, кастомный Select), используется компонент Controller: он связывает такое поле с формой, сохраняя декларативный API. Отдельного внимания заслуживает хук useFieldArray — он позволяет работать с динамическими списками полей, например при добавлении нескольких телефонов или адресов в одной форме.

Если хотите, чтобы ошибки показывались не сразу при вводе, а после того как пользователь покинул поле, задайте в useForm параметр mode: "onTouched" — так подсказки появятся раньше, чем при общем сабмите, но не будут раздражать во время самого набора текста.

Formik

Formik — один из первых по-настоящему популярных инструментов для форм в React. В отличие от React Hook Form, он изначально построен на контролируемых полях и активно использует React Context для передачи состояния формы вниз по дереву компонентов.

Компонент Formik и хук useFormik

Основной способ работы — компонент Formik, которому передаются начальные значения, схема валидации и обработчик отправки. Внутри него доступны компоненты Form, Field и ErrorMessage, которые автоматически подключаются к контексту формы.

npm install formik yup
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
 
const schema = Yup.object({
  email: Yup.string().email("Некорректный email").required("Обязательное поле"),
});
 
function SignupForm() {
  return (
    <Formik
      initialValues={{ email: "" }}
      validationSchema={schema}
      onSubmit={(values) => console.log(values)}
    >
      <Form>
        <Field name="email" type="email" />
        <ErrorMessage name="email" component="span" />
        <button type="submit">Отправить</button>
      </Form>
    </Formik>
  );
}

Каждое изменение в поле Field обновляет общий state формы, поэтому при большом количестве полей и активном вводе возможны лишние рендеры — это главная плата за удобный декларативный API.

Валидация через Yup

Чаще всего Formik используют вместе с библиотекой Yup: она описывает схему допустимых значений декларативно и хорошо интегрируется через параметр validationSchema. Такой подход упрощает поддержку сложных форм, потому что вся логика проверки собрана в одном месте, а не размазана по атрибутам каждого поля.

Final Form (React Final Form)

React Final Form — React-обёртка над библиотекой final-form, не зависящей от какого-либо UI-фреймворка. Её ключевая особенность — подписочная модель: каждое поле подписывается только на те изменения, которые его касаются, поэтому даже в контролируемом режиме лишние рендеры сведены к минимуму.

npm install react-final-form final-form
import { Form, Field } from "react-final-form";
 
function ProfileForm() {
  return (
    <Form
      onSubmit={(values) => console.log(values)}
      render={({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <Field name="username" component="input" placeholder="Имя пользователя" />
          <button type="submit">Сохранить</button>
        </form>
      )}
    />
  );
}

Компонент Form принимает функцию render (или использует children-as-function), а каждое поле оборачивается в компонент Field, который сам решает, когда обновлять связанный с ним UI. Такой подход особенно полезен в больших формах масштаба админ-панелей, где важно, чтобы изменение одного поля не перерисовывало соседние.

Сравнение библиотек

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

Критерий React Hook Form Formik Final Form
Тип полей Неконтролируемые Контролируемые Контролируемые с подпиской
Размер пакета Небольшой Средний Небольшой (ядро + обёртка)
Валидация Встроенные правила, Yup, Zod Yup, кастомные функции Кастомные функции, декораторы
Порог входа Средний Низкий Средний-высокий

При выборе конкретного инструмента стоит учитывать несколько факторов.

  1. Размер и сложность формы — для одного-двух полей разница между библиотеками почти не ощущается
  2. Важность производительности при частом вводе, например в формах с десятками полей
  3. Наличие уже настроенной схемы валидации на Yup в проекте
  4. Опыт команды и привычка к конкретному инструменту

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

Итоги

В этом уроке мы разобрали три основных инструмента для работы с формами в React — React Hook Form, Formik и Final Form — и увидели, что выбор между ними зависит от требований к производительности, размеру бандла и предпочтений команды.

В следующем уроке перейдём от форм к проверке кода и разберём инструменты тестирования — Jest, React Testing Library и Cypress.

Теги: