Назад
Назад к заметкам
· 4 мин чтения

Контролируемые и неконтролируемые React-компоненты: как выбрать

Краткое руководство по выбору контролируемых и неконтролируемых компонентов в React: плюсы, минусы и примеры кода.

reactformscontrolled-componentsuncontrolled-components

Если в проекте появились формы, а их поля начинают “запрыгивать” в состояние, то выбрать между контролируемым и неконтролируемым подходом бывает непросто. Контролируемые и неконтролируемые 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-компоненты это не взаимоисключающие варианты, а инструменты для разных задач. Выбирайте их осознанно, учитывая размер формы, требования к валидации и нагрузку на рендеринг, и ваш код станет предсказуемым и легким в поддержке.

Готов начать?

Выбери удобный способ связи — напиши напрямую или оставь заявку

Написать в Telegram

Отвечу в течение пары часов