Формы в React: Hook Form, Formik, Final Form
Работа с формами — одна из самых частых задач в интерфейсной разработке, а React не предлагает единого встроенного решения для неё. Именно поэтому вокруг форм выросла целая экосистема сторонних библиотек — от лёгких и производительных до максимально гибких и декларативных. В этом уроке разберём три самых популярных инструмента: 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, кастомные функции |
Кастомные функции, декораторы |
| Порог входа | Средний | Низкий | Средний-высокий |
При выборе конкретного инструмента стоит учитывать несколько факторов.
- Размер и сложность формы — для одного-двух полей разница между библиотеками почти не ощущается
- Важность производительности при частом вводе, например в формах с десятками полей
- Наличие уже настроенной схемы валидации на
Yupв проекте - Опыт команды и привычка к конкретному инструменту
Не смешивайте библиотеки форм в одном проекте без явной необходимости: даже если каждая из них хорошо решает свою задачу, разные подходы к рендерингу усложняют поддержку кода и увеличивают итоговый размер бандла.
Итоги
В этом уроке мы разобрали три основных инструмента для работы с формами в React — React Hook Form, Formik и Final Form — и увидели, что выбор между ними зависит от требований к производительности, размеру бандла и предпочтений команды.
В следующем уроке перейдём от форм к проверке кода и разберём инструменты тестирования — Jest, React Testing Library и Cypress.