Контролируемые и неконтролируемые React-компоненты: как выбрать
Краткое руководство по выбору контролируемых и неконтролируемых компонентов в React: плюсы, минусы и примеры кода.
Если в проекте появились формы, а их поля начинают “запрыгивать” в состояние, то выбрать между контролируемым и неконтролируемым подходом бывает непросто. Контролируемые и неконтролируемые React-компоненты позволяют гибко управлять формой, сохраняя баланс между контролем и производительностью.
Что такое контролируемый компонент?
Контролируемые и неконтролируемые React-компоненты: первый тип контролируемый. Он элемент формы, значение которого полностью управляется состоянием React. Любое изменение ввода проходит через onChange
, где вы обновляете state
, а value
берется из него.
function ControlledInput() {
const [email, setEmail] = React.useState("");
return (
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
);
}
Плюс вы знаете точное значение в любой момент и можете валидировать его “на лету”. Минус каждый ввод приводит к перерисовке, что в больших формах добавляет нагрузку.
Что такое неконтролируемый компонент?
Контролируемые и неконтролируемые React-компоненты: второй тип неконтролируемый. Он хранит свое значение в DOM, а React читает его только при необходимости через ref
.
function UncontrolledInput() {
const inputRef = React.useRef(null);
const handleSubmit = () => {
alert("Введено: " + inputRef.current.value);
};
return (
<>
<input type="text" ref={inputRef} defaultValue="Привет" />
<button onClick={handleSubmit}>Отправить</button>
</>
);
}
React не следит за каждым символом, поэтому перерисовок меньше, но мгновенная валидация усложняется.
Tip: Как работать с рефами, читайте в статье рефы в React.
Когда использовать каждый подход? (таблица сравнения)
Критерий | Контролируемый компонент | Неконтролируемый компонент |
---|---|---|
Синхронизация с state | Да value берется из state | Нет значение хранится в DOM |
Валидация “на лету” | Легко, через onChange | Требует чтения через ref |
Перфоманс в больших формах | Может тормозить при частом вводе | Быстрее, реже обновляет React |
Фокус/курсорные манипуляции | Требует дополнительных функций | Прямой доступ к DOM через ref |
Требования UI-библиотеки | Часто нужен (Formik, React Hook Form) | Подходит для простых полей |
Если форма небольшая и важна валидация берите контролируемый компонент. Для длинных списков файлов или автокомплитов лучше неконтролируемый вариант.
Практический пример: регистрационная форма
function RegisterForm() {
const [form, setForm] = React.useState({ name: "", email: "" });
const avatarRef = React.useRef(null);
const handleChange = (e) => {
const { name, value } = e.target;
setForm((prev) => ({ ...prev, [name]: value }));
};
const handleSubmit = (e) => {
e.preventDefault();
const avatarFile = avatarRef.current.files[0];
console.log("Данные:", { ...form, avatar: avatarFile });
// отправка на сервер …
};
return (
<form onSubmit={handleSubmit}>
<label>
Имя:
<input name="name" value={form.name} onChange={handleChange} />
</label>
<label>
Email:
<input
name="email"
type="email"
value={form.email}
onChange={handleChange}
/>
</label>
<label>
Аватар:
<input type="file" ref={avatarRef} />
</label>
<button type="submit">Зарегистрироваться</button>
</form>
);
}
В примере используются контролируемые и неконтролируемые React-компоненты: поля “Имя” и “Email” контролируемые, а загрузка аватара неконтролируемый <input type="file">
. Такой гибрид часто встречается в реальных проектах.
Как работать с рефами и получать значение поля
Для неконтролируемых компонентов ключевой инструмент рефы. Создайте их через React.useRef
и передайте в ref
-prop. После монтирования ref.current
содержит DOM-элемент.
const ageRef = React.useRef<HTMLInputElement>(null);
function submit() {
const age = ageRef.current?.value; // строка
console.log("Возраст:", age);
}
ref
может быть null
до монтирования, поэтому проверяйте его наличие.
Когда рефы действительно спасают:
- При работе с файловыми инпутами.
- При интеграции сторонних UI-библиотек, управляющих элементом самостоятельно.
- При необходимости управлять фокусом или скроллом без перерисовки.
FAQ / Чеклист
- Не смешивайте стратегии в одном поле. Выберите либо
value
/onChange
, либоdefaultValue
+ref
. - Не храните в
state
большие блобы файлов. Используйте неконтролируемый<input type="file">
и получайте файл черезref
. - Следите за производительностью. При большом количестве контролируемых полей применяйте
React.memo
или библиотеки вроде React Hook Form. - Указывайте
defaultValue
у неконтролируемых элементов. Это задает начальное состояние без привязки кstate
. - Проверяйте
ref.current
наnull
. При сервер-сайд рендеринге реф будет пустым.
Контролируемые и неконтролируемые React-компоненты это не взаимоисключающие варианты, а инструменты для разных задач. Выбирайте их осознанно, учитывая размер формы, требования к валидации и нагрузку на рендеринг, и ваш код станет предсказуемым и легким в поддержке.