Ты просишь Claude Code собрать форму обратной связи — три поля, кнопка «Отправить». В ответ появляется код, где вместо привычного <form> с парой <input> внутри стоят незнакомые вызовы: useForm(), register("email"), handleSubmit(onSubmit). Ни одного из этих слов не было в твоей просьбе, но без них форма почему-то не работает. Это React Hook Form — библиотека, которую ИИ подключает почти к каждой форме сложнее одного поля. Разберём, зачем она нужна и что означает каждая из этих загадочных строк.
Формы на курсе появляются раньше, чем кажется: заявка на консультацию на лендинге, форма заказа в интернет-магазине, редактирование профиля в личном кабинете. Как только полей становится больше одного, а хотя бы одно из них нужно проверять на корректность, у Claude Code почти всегда находится повод подключить именно React Hook Form — и полезно понимать, что именно он в этот момент делает с твоим кодом, а не просто доверять, что «так надо».
Содержание
- Что делает register() в коде, который написал ИИ
- Почему нельзя было обойтись обычным
- Что делает React Hook Form простыми словами
- Валидация через zod: ещё одна частая пара в сгенерированном коде
- При чём тут shadcn/ui и компонент Controller
- Обычный useState-подход и React Hook Form — что меняется
- Когда это оправдано, а когда хватит обычного useState
- Как проверить, что форма собрана правильно
- Частые вопросы
- Заключение
Что делает register() в коде, который написал ИИ
Открой файл с формой, которую собрал Claude Code. Скорее всего, он выглядит примерно так:
const { register, handleSubmit, formState: { errors } } = useForm();
function onSubmit(data) {
console.log(data);
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("email", { required: "Введите email" })} />
{errors.email && <p>{errors.email.message}</p>}
<button type="submit">Отправить</button>
</form>
);
Ничего из этого не сломано и не лишнее. useForm() — точка входа: вызов, который создаёт для формы отдельный внутренний «блокнот», где React Hook Form будет хранить всё, что происходит с полями — значения, ошибки, факт того, что поле уже трогали. Из этого блокнота ты забираешь три инструмента, которые понадобятся дальше: register, handleSubmit и formState.
register("email", ...) — это не создание поля, а подключение уже существующего <input> к блокноту формы. Строчка {...register("email")} разворачивается в набор обычных HTML-атрибутов: name, onChange, onBlur, ref. Ты как будто говоришь библиотеке: «вот это поле зовут email, начни следить за ним». Второй аргумент, объект с required: "Введите email", — правило проверки: если поле останется пустым, ошибка появится с этим текстом.
handleSubmit(onSubmit) оборачивает твою функцию отправки. Когда пользователь жмёт кнопку, handleSubmit сначала сам проверяет все зарегистрированные поля по их правилам. Если хоть одно не прошло проверку — функция onSubmit вообще не вызывается, вместо этого заполняется объект errors. Если все поля в порядке — onSubmit получает готовый объект data со значениями всех полей формы, без единой строчки кода, которая бы их туда складывала вручную.
formState.errors — тот самый объект с ошибками, о котором уже шла речь. errors.email появляется, только если поле email не прошло проверку, и содержит текст ошибки, который ты задал в register. Строчка {errors.email && <p>{errors.email.message}</p>} — обычное условие React: «если ошибка есть, покажи параграф с текстом ошибки», знакомое из ранних уроков курса про «Змейку».
Собранная вместе, эта форма делает три вещи одновременно: отслеживает значения полей, проверяет их по правилам при отправке и показывает понятные ошибки прямо под нужным полем. Без единой строчки useState.
Обрати внимание и на саму запись {...register("email")} — три точки перед вызовом называются spread-синтаксис, обычная возможность JavaScript «распаковать» объект в отдельные атрибуты. register возвращает объект вроде { name: "email", onChange: ..., onBlur: ..., ref: ... }, а три точки превращают его в набор именно этих атрибутов прямо на элементе <input>, как если бы каждый был прописан отдельно. Ничего специфичного для React Hook Form в этом синтаксисе нет — та же запись пригодится и в других местах, где нужно быстро передать элементу целый набор готовых свойств разом.
Полезно знать ещё пару частых спутников register и handleSubmit, которые тоже попадаются в сгенерированном коде. formState.isSubmitting — булево значение, которое становится true на время отправки формы: удобно, чтобы заблокировать кнопку и не дать пользователю отправить форму дважды подряд. reset() очищает все поля до значений по умолчанию — обычно вызывается сразу после успешной отправки, чтобы форма не осталась заполненной старыми данными. defaultValues, который передают вторым аргументом в useForm({ defaultValues: {...} }), задаёт стартовые значения полей — пригождается в форме редактирования, где поля должны открыться уже заполненными текущими данными, а не пустыми.