Menu

Формы в React: поля ввода, флажки, select и валидация

Формы в React на контролируемых полях: текстовые поля, textarea, select, флажки и переключатели, один объект состояния для многих полей, отправка через onSubmit, показ ошибок валидации и действия форм в React 19.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Форма в React хранит значение каждого поля в состоянии: поле показывает value={state} и обновляет его через onChange. Когда пользователь отправляет форму, обработчик onSubmit вызывает e.preventDefault(), чтобы остановить перезагрузку страницы, и затем использует значения, которые уже лежат в состоянии.

Введите имя и нажмите Enter или кнопку. Затем удалите строку e.preventDefault();: превью покажет поля, которые браузер отправил бы при нативной отправке, а на настоящей странице это ещё и перезагрузило бы её.

Поле ввода, значение которого берётся из состояния, называется контролируемым. Альтернатива, когда DOM хранит значение, а вы читаете его позже, разобрана на странице о контролируемых и неконтролируемых компонентах.

Текстовые поля и textarea

Все текстовые поля работают одинаково: value из состояния, onChange читает e.target.value. <textarea> следует тому же шаблону. В HTML его текст стоит между тегами, а в React его передают как value.

Поскольку значение задаёте вы сами, можно изменить то, что набрал пользователь, до того как это появится: поле заголовка превращает каждую букву в заглавную. Счётчик символов обновляется при каждом нажатии клавиши, потому что body это состояние, и каждое изменение вызывает новый рендер.

Select

<select> принимает value на самом элементе select, а не selected на option. e.target.value это value выбранного варианта.

Для множественного выбора добавьте multiple и передайте массив как value. Читайте выбранные варианты через Array.from(e.target.selectedOptions, (o) => o.value).

Флажки и переключатели

Флажок контролируется через checked, а не value, и его обработчик читает e.target.checked, то есть true или false. Переключатели (radio) в группе имеют общий name; каждый отмечен, когда состояние равно его значению.

Читать e.target.value у флажка это частая ошибка: для флажка без атрибута value оно возвращает строку "on", отмечен он или нет.

Один объект состояния для многих полей

Форме с десятью полями не нужны десять вызовов useState и десять обработчиков. Храните один объект, дайте каждому полю name, совпадающий с ключом, и напишите один обработчик, который использует имя как вычисляемый ключ: [e.target.name].

Обработчик проверяет тип поля, чтобы флажок сохранял булево значение, а остальные текст. Чтобы добавить поле, добавьте ключ в начальный объект и поле с тем же name; обработчик менять не нужно. Spread сохраняет все остальные поля (см. обновление массивов и объектов).

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

Ставьте логику отправки в onSubmit формы, а не в onClick кнопки. onSubmit срабатывает и тогда, когда пользователь нажимает Enter в текстовом поле, а обработчик клика это пропустил бы. <button> внутри формы по умолчанию является кнопкой отправки; другим кнопкам давайте type="button", чтобы они не отправляли форму.

Откройте консоль, введите пароль и нажмите Enter: форма выведет весь объект. Клик по «Clear» ничего не выводит, потому что это type="button". Удалите этот атрибут, и Clear тоже начнёт отправлять форму.

Валидация и показ ошибок

Простая валидация это функция, которая принимает значения и возвращает объект сообщений. Запускайте её при отправке, сохраняйте результат в состоянии и показывайте каждое сообщение под его полем. Отключайте кнопку отправки, пока выполняется запрос, чтобы двойной клик не отправил форму дважды.

Нажмите Save с пустыми полями, чтобы увидеть оба сообщения, затем заполните их. saveProfile заменяет настоящий запрос: он ждёт 800 миллисекунд и завершается успешно. Атрибут noValidate отключает собственные подсказки валидации браузера, чтобы показывались только ваши сообщения; уберите его и добавьте required к полю, чтобы сравнить.

Ошибки можно и вычислять во время рендера, а не хранить, например const emailError = form.email && !form.email.includes('@'), что показывает сообщение по мере ввода. Выбирайте момент, который подходит форме: при отправке спокойнее, а при вводе обратная связь быстрее.

Действия форм в React 19

React 19 добавляет ещё один способ обработать форму. Передайте функцию в проп action формы. React сам предотвращает перезагрузку страницы и вызывает функцию с объектом FormData, в котором лежит каждое поле с name. Полям вообще не нужно состояние: они неконтролируемые, и React очищает их после завершения действия.

Напишите свой комментарий и опубликуйте его. Список показывает ваш текст, а поле возвращается к Nice docs: после завершения действия React сбрасывает неконтролируемые поля к их defaultValue. Действие может быть асинхронным, и пока оно выполняется, React считает обновление переходом (transition).

Чтобы показать состояние ожидания или ошибку из действия, оберните его в useActionState, который возвращает последний результат, обёрнутое действие для формы и флаг isPending. Кнопка глубоко внутри формы тоже может прочитать состояние ожидания через useFormStatus из react-dom:

import { useFormStatus } from 'react-dom';

function SubmitButton() {
    const { pending } = useFormStatus();
    return <button disabled={pending}>{pending ? 'Posting...' : 'Post'}</button>;
}

useFormStatus не загружен в редакторах здесь, поэтому он показан только в этом статическом блоке. Его нужно вызывать из компонента, отрендеренного внутри <form>, а не в компоненте, который рендерит форму.

Контролируемое состояние с onSubmit и действия форм с FormData оба работают в React 19. Контролируемые поля подходят формам, которые реагируют во время ввода (живая валидация, счётчик символов, поля, зависящие друг от друга). Действия подходят формам, где значения нужны только при отправке.

Часто задаваемые вопросы

Как получить значение поля ввода в React?

Храните его в состоянии и обновляйте из onChange: <input value={name} onChange={e => setName(e.target.value)} />. Тогда текущий текст всегда лежит в name. Для флажков читайте e.target.checked вместо e.target.value.

Как запретить форме перезагружать страницу в React?

Вызовите e.preventDefault() в начале обработчика onSubmit. Без этого браузер выполняет обычную отправку формы, которая переходит на другую страницу и перезагружает её.

Как обрабатывать много полей одним состоянием в React?

Храните в состоянии один объект, дайте каждому полю name, совпадающий с ключом, и используйте один обработчик с вычисляемым ключом: setForm({ ...form, [e.target.name]: e.target.value }).

Почему textarea в React использует value, а не children?

В HTML текст textarea стоит между тегами, а в React его передают как value (или defaultValue), так же как у input. Так каждое текстовое поле в форме React работает одинаково.

Что такое действия форм в React 19?

В React 19 можно передать функцию в <form action={fn}>. React предотвращает перезагрузку страницы, вызывает функцию с FormData формы и по завершении сбрасывает неконтролируемые поля. useActionState добавляет поверх этого флаг ожидания и результат.

Иллюстрация языков программирования Coddy

Учитесь программировать с Coddy

НАЧАТЬ