Мы уже прошли большой путь, изучили JSX, компоненты, state и props. Сегодня нас ждет одна из часто используемых тем в веб-разработке, это управление формами.
Представьте себе любую современную веб-страницу. Вход в личный кабинет, регистрация, поиск, добавление товара в корзину, комментарии в соцсетях, все это формы. В React есть два основных подхода к работе с ними. Использование управляемых и неуправляемых компонентов. В этом уроке мы сфокусируемся на первом, рекомендуемом способе, это управляемых компонентах.
Что такое управляемые компоненты?
Управляемый компонент, это такой компонент формы, данные которого полностью управляются состоянием (state) React. Вместо того чтобы самому искать в DOM, какое значение ввел пользователь (как это бывает в обычном HTML), мы сохраняем каждое изменение в state, а затем из этого state подставляем значение обратно в поле ввода.
Звучит немного запутанно? Давайте разберемся на простой аналогии. Представьте, что вы дирижер оркестра (React-приложение), а музыканты (элементы формы) это ваши инструменты. Вы не просто даете им ноты и надеетесь, что они сыграют правильно. Вы слышите каждый звук (изменение в поле ввода) и если он вам не нравится, вы тут же даете указание сыграть иначе (обновляете state и поле ввода). Вы полностью контролируете весь процесс. Это и есть управляемый компонент.
В техническом плане это означает, что у каждого поля формы (например, <input>) есть два ключевых атрибута:
-
value. Его значение привязывается к переменной в state. -
onChange. Обработчик события, который вызывается при любом изменении значения в поле. Этот обработчик обновляет state.
Таким образом, получается замкнутый цикл данных:
-
Пользователь набирает символ в
<input>. -
Срабатывает событие
onChange. -
Обработчик
onChangeвызывает функциюsetState()для обновления состояния. -
Компонент перерисовывается с новым значением state.
-
<input>получает обновленное значение из state через атрибутvalue.
Этот подход делает React «источником истины» для данных в форме. Все данные хранятся в state и интерфейс всегда им соответствует.
Связывание state с полями ввода (input)
Давайте перейдем от теории к практике. Самый распространенный элемент формы, это текстовое поле <input type="text">. Создадим простейший управляемый компонент.
import React, { useState } from 'react'; function SimpleInput() { // 1. Создаем состояние для хранения значения input const [inputValue, setInputValue] = useState(''); // 2. Обработчик для события onChange const handleInputChange = (event) => { // event.target это DOM-элемент input // event.target.value содержит текущее значение input setInputValue(event.target.value); }; // 3. Обработчик для отправки формы const handleSubmit = (event) => { event.preventDefault(); // Предотвращаем перезагрузку страницы alert(`Введенное имя: ${inputValue}`); }; return ( <form onSubmit={handleSubmit}> <label> Ваше имя: {/* 4. Связываем input с state: value и onChange */} <input type="text" value={inputValue} onChange={handleInputChange} /> </label> <button type="submit">Отправить</button> {/* 5. Показываем текущее значение для наглядности */} <p>Текущее значение: {inputValue}</p> </form> ); } export default SimpleInput;
Давайте разберем этот код по шагам, чтобы все было кристально понятно:
-
Инициализация состояния. Мы используем хук
useState, чтобы создать переменную состоянияinputValueи функцию для ее обновленияsetInputValue. Начальное значение (пустая строка). -
Обработчик изменения
handleInputChange. Эта функция вызывается каждый раз, когда пользователь что-то вводит в поле. Параметрeventсодержит информацию о событии. Нам критически важно свойствоevent.target.valueэто то, что пользователь ввел на данный момент. Мы берем это значение и немедленно обновляем наш state, вызываяsetInputValue(event.target.value). -
Обработчик отправки формы
handleSubmit. Стандартное поведение формы, отправить данные на сервер и перезагрузить страницу. В одностраничных приложениях (SPA) это нежелательно.event.preventDefault()отменяет это поведение. Далее мы можем делать с данными что угодно, например, отправить их на сервер с помощью fetch или вывести в alert. -
Связывание в JSX. В самом теге
<input>мы устанавливаем два ключевых атрибута.-
value={inputValue}. По React: «Значение этого поля всегда должно быть равно тому, что хранится вinputValue». -
onChange={handleInputChange}. По React: «При любом изменении вызывай функциюhandleInputChange».
-
-
Отображение состояния. Для демонстрации мы выводим текущее значение
inputValueв теге<p>. Это наглядно показывает, как состояние синхронизировано с полем ввода.
Попробуйте этот код в вашем проекте. Вы увидите, что как только вы начинаете печатать, текст под формой мгновенно обновляется. Это и есть простота управляемых компонентов в действии, полный контроль и синхронизация.
Работа с textarea
В обычном HTML элемент <textarea> определяет свое содержимое через дочерний текст:
<textarea> Какой-то текст внутри textarea </textarea>
В React, для единообразия и чтобы сделать <textarea> управляемым компонентом, мы также используем атрибут value. Это гораздо удобнее, так как позволяет управлять многострочным вводом точно так же, как и обычным текстовым полем.
import React, { useState } from 'react'; function FeedbackForm() { const [feedback, setFeedback] = useState(''); const handleFeedbackChange = (event) => { setFeedback(event.target.value); }; const handleSubmit = (event) => { event.preventDefault(); console.log('Отзыв отправлен:', feedback); // Здесь обычно отправка на сервер setFeedback(''); // Очистка формы после "отправки" }; return ( <form onSubmit={handleSubmit}> <label> Ваш отзыв: {/* Используем value, а не детей */} <textarea value={feedback} onChange={handleFeedbackChange} rows={5} cols={50} /> </label> <br /> <button type="submit">Отправить отзыв</button> <div> <h4>Предпросмотр вашего отзыва:</h4> <p>{feedback}</p> </div> </form> ); } export default FeedbackForm;
Как видите, здесь нет никакой принципиальной разницы с <input type="text">. Мы создаем состояние feedback, привязываем его к <textarea> через value и обновляем с помощью onChange. Такой подход объединяет логику работы с различными полями ввода, делая код предсказуемым и простым для понимания.
Работа с выпадающим списком (select)
С элементом <select> в React история похожая. В HTML вы выбираете option по атрибуту selected:
<select> <option value="apple">Яблоко</option> <option value="banana" selected>Банан</option> <!-- Выбран по умолчанию --> <option value="cherry">Вишня</option> </select>
В управляемом компоненте React мы указываем выбранное значение не в каждом <option>, а в родительском <select> с помощью атрибута value. Это самый декларативный и контролируемый способ.
import React, { useState } from 'react'; function FruitSelector() { const [selectedFruit, setSelectedFruit] = useState('banana'); // Устанавливаем значение по умолчанию const handleFruitChange = (event) => { setSelectedFruit(event.target.value); }; const handleSubmit = (event) => { event.preventDefault(); alert(`Вы выбрали: ${selectedFruit}`); }; return ( <form onSubmit={handleSubmit}> <label> Выберите фрукт: {/* value управляет выбранным option */} <select value={selectedFruit} onChange={handleFruitChange}> <option value="apple">Яблоко</option> <option value="banana">Банан</option> <option value="cherry">Вишня</option> <option value="orange">Апельсин</option> </select> </label> <button type="submit">Подтвердить выбор</button> <p>Текущий выбор: <strong>{selectedFruit}</strong></p> </form> ); } export default FruitSelector;
Что здесь происходит? Мы создаем состояние selectedFruit и устанавливаем ему начальное значение 'banana'. В теге <select> мы говорим: «Твое выбранное значение, это то, что лежит в selectedFruit». При изменении выбора (событие onChange) мы обновляем state. React следит за этим и автоматически делает активным тот <option>, у которого атрибут value совпадает с selectedFruit.
Этот подход также легко расширяется для работы с множественным выбором. Для этого нужно указать атрибут multiple={true} в <select>, а в value передавать массив выбранных значений.
function MultiSelect() { const [selectedColors, setSelectedColors] = useState(['red', 'blue']); const handleColorChange = (event) => { // Для множественного выбора event.target.selectedOptions, это коллекция выбранных option. // Преобразуем ее в массив значений. const selectedOptions = Array.from(event.target.selectedOptions).map(option => option.value); setSelectedColors(selectedOptions); }; return ( <div> <select multiple={true} value={selectedColors} onChange={handleColorChange}> <option value="red">Красный</option> <option value="green">Зеленый</option> <option value="blue">Синий</option> <option value="yellow">Желтый</option> </select> <p>Выбранные цвета: {selectedColors.join(', ')}</p> </div> ); }
Обработка нескольких полей ввода
В реальной форме полей почти всегда больше одного. Создавать отдельный обработчик для каждого поля, не самый эффективный путь. Вместо этого мы можем использовать единственный обработчик, который будет определять, какое поле изменилось и обновлять соответствующий кусочек state.
Самый простой способ сделать это, использовать синтаксис вычисляемых имен свойств в объектах. Давайте создадим форму регистрации.
import React, { useState } from 'react'; function RegistrationForm() { // 1. Создаем единый объект состояния для всех полей формы const [formData, setFormData] = useState({ firstName: '', lastName: '', email: '', password: '' }); // 2. Создаем один универсальный обработчик для всех полей const handleInputChange = (event) => { // Получаем имя поля и его значение const { name, value } = event.target; // Обновляем state. Используем функцию обновления для гарантированной работы с актуальным state setFormData(prevFormData => { return { ...prevFormData, // Копируем все предыдущие поля [name]: value // Обновляем конкретное поле, используя его name }; }); }; const handleSubmit = (event) => { event.preventDefault(); // В реальном приложении здесь был бы fetch-запрос console.log('Данные для регистрации:', formData); alert(`Регистрация прошла успешно для ${formData.firstName} ${formData.lastName}!`); // Очистка формы setFormData({ firstName: '', lastName: '', email: '', password: '' }); }; return ( <form onSubmit={handleSubmit}> <div> <label> Имя: <input type="text" name="firstName" // Важно: атрибут name должен совпадать с ключом в state value={formData.firstName} onChange={handleInputChange} /> </label> </div> <div> <label> Фамилия: <input type="text" name="lastName" value={formData.lastName} onChange={handleInputChange} /> </label> </div> <div> <label> Email: <input type="email" name="email" value={formData.email} onChange={handleInputChange} /> </label> </div> <div> <label> Пароль: <input type="password" name="password" value={formData.password} onChange={handleInputChange} /> </label> </div> <button type="submit">Зарегистрироваться</button> </form> ); } export default RegistrationForm;
Ключевые моменты этого подхода:
-
Единый объект state. Все данные формы хранятся в одном объекте
formData. Это очень удобно, так как при отправке формы нам не нужно собирать данные из разных кусочков state (они уже все вместе). -
Атрибут
name. Каждому полю ввода мы задаем уникальный атрибутname, который точно соответствует имени свойства в нашем объектеformData(firstName,lastNameи т.д.). -
Универсальный обработчик
handleInputChange:-
С помощью деструктуризации мы из
event.targetизвлекаемname(имя поля) иvalue(его значение). -
Далее мы обновляем state. Мы используем функцию обновления для
setFormData, которая принимает предыдущее состояние (prevFormData). Это лучшая практика, особенно если обновления state могут быть асинхронными. -
Мы возвращаем новый объект. Сначала оператором (
...prevFormData) копируем все старые поля, а затем перезаписываем одно конкретное поле[name]: value. Квадратные скобки вокруг[name]это и есть вычисляемое свойство, которое позволяет использовать значение переменнойnameкак ключ объекта.
-
Этот паттерн невероятно мощен и масштабируем. Вы можете добавить в форму десятки новых полей и вам не придется создавать для них новые обработчики, handleInputChange справится со всеми.
Практические задачи для закрепления
Давайте закрепим полученные знания на реальных примерах. Выполните эти задачи самостоятельно, это сильно прокачает ваш навык работы с формами в React.
Задача 1: Поиск с предпросмотром
Создайте компонент поиска. Пользователь вводит запрос в поле и ниже мгновенно отображается результат поиска (пока что просто текст: «Вы ищете: [запрос]»). Добавьте кнопку «Найти», при нажатии на которую выводится alert с итоговым запросом.
Подсказки:
-
Используйте управляемый компонент для input.
-
Для предпросмотра просто выводите значение из state в теге
<p>или<div>. -
Не забудьте
event.preventDefault()в обработчике кнопки.
Задача 2: Форма обратной связи с селектом и текстовой областью
Создайте форму обратной связи, которая включает:
-
Выпадающий список (
<select>) для выбора темы обращения (например, «Техническая проблема», «Вопрос по оплате», «Общее пожелание»). -
Текстовую область (
<textarea>) для самого сообщения. -
Поле для ввода email.
-
Кнопку отправки.
При отправке формы выводите в консоль объект со всеми собранными данными.
Подсказки:
-
Используйте паттерн с единым объектом state и универсальным обработчиком, как в примере с формой регистрации.
-
Для
<select>не забудьте указать атрибутnameи привязать егоvalueк state.
Задача 3 (продвинутая): Простая валидация формы
Расширьте форму регистрации из нашего примера. Добавьте простую валидацию:
-
Поля «Имя» и «Фамилия» не должны быть пустыми.
-
Email должен содержать символ
@. -
Пароль должен быть не короче 6 символов.
Выводите сообщения об ошибках под соответствующими полями, но только после того, как пользователь попытался отправить форму (или после того, как он покинул поле, это называется «onBlur»). Кнопка «Зарегистрироваться» должна быть неактивной (атрибут disabled), пока есть ошибки валидации.
Подсказки:
-
Создайте отдельный state для ошибок, например,
const [errors, setErrors] = useState({}), где ключи это имена полей, а значения строки с ошибками. -
В обработчике
handleSubmitперед отправкой проверяйте данные и заполняйте объектerrors. -
В JSX под каждым полем выводите
errors.fieldName, если ошибка для этого поля существует. -
Условие для атрибута
disabledкнопки может быть таким:disabled={Object.keys(errors).length > 0}.
На сегодня все! Мы подробно разобрали, что такое управляемые компоненты и как с их помощью создавать мощные, предсказуемые и удобные формы в React. Вы научились работать с текстовыми полями, textarea, select и эффективно обрабатывать формы с множеством полей.
Как всегда, если есть вопросы, пишите в комментариях.
Хотите освоить React полностью? Переходи к полному курсу с уроками по React для начинающих, где мы с нуля разбираем все тонкости этой мощной библиотеки, пишем реальные проекты и закрепляем знания на практике.
Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.


