Работа с формами в React
Работа с формами и элементами формы в React требует внимания — HTML-элементы форм ведут себя иначе, чем обычные DOM-элементы. В этой статье показаны практические приёмы для управления полями ввода, textarea и чекбоксами, примеры корректного кода, распространённые ошибки и рекомендации по тестированию и доступности.
Основная идея и связанные фразы
Основной запрос: управление формами в React Связанные варианты запросов: контролируемые компоненты React, textarea в React, checkbox в React, обработка форм React, формы React Hook Form, валидация форм в React
Управление полями ввода в формах
В React управление полем ввода обычно реализуется через состояние компонента и привязку значения поля к этому состоянию. Это делает React-состояние единственным источником правды: значение поля всегда берётся из state.
Простой пример для одного поля (корректный паттерн с onChange):
function App() {
const [firstName, setFirstName] = React.useState('');
function handleFirstNameChange(event) {
setFirstName(event.target.value);
}
return (
);
}Важно: в реальных приложениях обычно используют onChange для текстовых полей — это стандартный и ожидаемый обработчик в React. onInput тоже существует, но onChange — более распространённый выбор.
Когда один state на поле не подходит
Если у вас много полей, создавать отдельные состояния и обработчики для каждого — громоздко. Решение — хранить объект с данными формы и использовать единый обработчик, который обновляет нужное свойство по name поля.
function App() {
const [formData, setFormData] = React.useState({
firstName: '',
lastName: ''
});
function handleChange(event) {
const { name, value } = event.target;
setFormData(prev => ({
...prev,
[name]: value
}));
}
return (
);
}Ключевое правило: имена свойств в объекте состояния должны совпадать с атрибутами name у input.
Превращаем поля ввода в контролируемые компоненты
HTML-форма по умолчанию при отправке выполняет переход на новую страницу. В React чаще требуется предотвратить это и обрабатывать данные в JavaScript (например, валидировать и отправлять через fetch). Контролируемый компонент — это input/textarea/select, значение которого полностью задаётся значением из state через prop value (для checkbox — checked).
Пример с обработкой сабмита:
function App() {
const [formData, setFormData] = React.useState({
firstName: '',
lastName: ''
});
function handleChange(event) {
const { name, value } = event.target;
setFormData(prev => ({ ...prev, [name]: value }));
}
function handleSubmit(event) {
event.preventDefault();
// валидировать formData и отправить на сервер
console.log('Отправляем форму', formData);
}
return (
);
}Совет: вынесите логику в кастомный хук useForm для повторного использования.
Работа с textarea
Тег textarea в «чистом» HTML определяет своё содержание как children. В React textarea тоже возможен как контролируемый компонент — у него есть value и onChange, но необходимо использовать сам тег
Правильный пример:
function App() {
const [formData, setFormData] = React.useState({ message: 'Привет! Как дела?' });
function handleChange(event) {
const { name, value } = event.target;
setFormData(prev => ({ ...prev, [name]: value }));
}
return (
);
}Ошибка, которую часто встречают: использование — это неверно и не создаёт многострочного поля.
Чекбоксы и переключатели
Чекбокс отличается от текстовых полей: у него нет значения в виде строки (хотя у checkbox может быть value), но ключевой prop для контролируемого чекбокса — checked (boolean).
Простой пример:
function App() {
const [isChecked, setIsChecked] = React.useState(false);
function handleChange() {
setIsChecked(prev => !prev);
}
return (
);
}Если в форме есть разные типы полей, объедините логику в общем обработчике, учитывая type и checked:
function App() {
const [formData, setFormData] = React.useState({
firstName: '',
join: true
});
function handleChange(event) {
const { name, value, type, checked } = event.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
}
return (
);
}Заметка: важно сравнивать type === ‘checkbox’ (строка в кавычках). Если забыть кавычки, получите ошибку.
Доступность и метки
- Всегда связывайте label с input через htmlFor / id: это улучшает доступность и кликабельность.
- Для чекбоксов и радиокнопок используйте понятные тексты и aria-атрибуты при необходимости.
- Для ошибок валидации показывайте текст ошибки в элементе, который связан с полем через aria-describedby.
Important: корректная разметка и семантика заметно повышают удобство для пользователей экранных читалок.
Частые ошибки и как их исправлять
- Использование input type=’textarea’ вместо тега
- Забвение value/checked при контролируемом компоненте — поле перестанет обновляться. Решение: привяжите value/checked к state.
- Смешивание onInput и onChange без понимания различий. Рекомендуется onChange.
- Неправильная проверка типа чекбокса (type === checkbox). Нужно type === ‘checkbox’.
- Мутация состояния формы напрямую (например, prevState.field = value) — используйте возвращаемый объект и spread: setFormData(prev => ({ …prev, [name]: value })).
Альтернативные подходы
Неуправляемые компоненты + refs: используйте рефы (useRef) для простых форм, где не требуется постоянный рендер при вводе. Это снижает число ререндеров, но усложняет валидацию и отслеживание изменений.
Библиотеки для форм:
- React Hook Form — оптимизирован по производительности, рекомендуют для больших, динамичных форм.
- Formik — зрелая библиотека с богатым набором возможностей и плагинов.
Выбор: если у вас много полей и сложная валидация — используйте библиотеку; для простой формы — контролируемые компоненты + кастомный хук достаточно.
Производительность и масштабирование
- При большом количестве полей используйте useReducer или библиотеки (React Hook Form) для минимизации перерасчётов компонентов.
- Дебаунс валидации полей при вводе (если выполняете тяжёлые операции, например, проверку уникальности имени на сервере).
- Мемоизация обработчиков: useCallback для handleChange может помочь, если передаёте обработчик глубоко вниз по дереву.
Тестирование форм — примеры кейсов
- Отправка пустой формы: ожидаем предотвратить отправку и показать ошибки валидации.
- Пошаговая валидация: ввод корректного и некорректного значения в поле, проверка текста ошибки.
- Чекбокс: при клике меняется состояние checked и значение в formData.
- Textarea: ввод многострочного текста сохраняется в state.
- Сабмит: onSubmit получает ожидаемый объект formData и вызывает сетевой запрос (mock).
Критерии приёмки:
- Поля корректно привязаны к state.
- Ошибки валидации отображаются и доступны через aria.
- При submit событие предотвращает переход и выполняет обработчик.
Простая методология / чек-лист при разработке формы
- Определите структуру данных (какие поля и типы).
- Выберите стратегию: контролируемые компоненты или библиотека.
- Реализуйте state (объект или useReducer).
- Свяжите поля через name и value/checked.
- Добавьте обработчик onChange и onSubmit (event.preventDefault()).
- Реализуйте валидацию и отображение ошибок.
- Проверьте доступность (label, aria-атрибуты).
- Напишите тесты (unit/integration).
Ментальные модели и эвристики
- «Single source of truth»: состояние формы должно быть в одном месте — либо DOM (неуправляемые), либо React state.
- «Контролируемый — предсказуемый»: контролируемые поля упрощают синхронизацию, но могут увеличить количество ререндеров.
- «Минимизируй обновления»: если ререндеры заметно грузят интерфейс, переходите на useRef/неуправляемые поля или библиотеку.
Короткий глоссарий (1 строка)
- Контролируемый компонент: input/textarea/select, значение которого задаётся через React state.
- Неуправляемый компонент: поле, которое хранит собственное значение в DOM; доступ через ref.
Примеры для реальных сценариев (шаблоны)
- Простая форма регистрации (контролируемая): используйте объект formData + handleChange выше.
- Форма с несколькими группами чекбоксов: храните массив выбранных значений и управляйте добавлением/удалением в обработчике.
Когда подход с контролируемыми компонентами не годится
- Очень большие формы с сотнями полей и частыми вводами: контролируемые компоненты могут вызывать излишние ререндеры. Решение — React Hook Form или неуправляемые поля.
- Если нужно получить текущее значение поля реже (например, при submit) — рефы проще.
Пример использования React Hook Form (микро-демо)
import { useForm } from 'react-hook-form';
function App() {
const { register, handleSubmit } = useForm();
function onSubmit(data) {
console.log(data);
}
return (
);
}Этот подход уменьшает количество ререндеров и делает работу с валидацией более удобной.
Итог
Вы узнали основные принципы работы с формами в React: как делать контролируемые компоненты, как работать с textarea и checkbox, какие ошибки встречаются наиболее часто и как масштабировать форму с помощью библиотек или оптимизаций. Начинайте с простого управляемого объекта состояния, а по мере роста приложения переходите к библиотекам и оптимизациям.
Ключевые рекомендации:
- Всегда связывайте name и ключ состояния.
- Для текстовых полей используйте value + onChange.
- Для чекбоксов используйте checked + onChange и проверяйте type === ‘checkbox’.
- Для больших форм рассматривайте React Hook Form или Formik.
Notes: тестируйте поведение формы и проверяйте доступность для всех пользователей.