В чем разница между контролируемыми и неконтролируемыми компонентами?
Компоненты
Разница в том, где живёт значение поля: у контролируемого — в state React (единственный источник правды), у неконтролируемого — в самом DOM.
Контролируемый
const [email, setEmail] = useState('');
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
/>Каждое нажатие: onChange → setState → перерендер → input отображает state. React полностью контролирует значение.
Неконтролируемый
const emailRef = useRef(null);
<input ref={emailRef} defaultValue="" />
// значение читается в нужный момент: emailRef.current.valueЗначением владеет DOM; React читает его по требованию (обычно на submit).
Отличия
- Источник правды: state против DOM.
- API:
value+onChangeпротивdefaultValue+ref. - Перерендеры: на каждый ввод против нуля.
- Живая валидация/маски/зависимые поля: легко против «вручную по событиям».
- Программное изменение значения:
setEmail('')против императивногоref.current.value = ''/form.reset().
Когда что выбирать
- Контролируемые: валидация по мере ввода, форматирование (маска телефона), зависимые поля, синхронизация значения с другим UI.
- Неконтролируемые: простые формы «заполнил — отправил», большие формы, где важна производительность, интеграция с нативным поведением; file input — только так.
- Библиотеки форм: Formik — контролируемый подход, React Hook Form — неконтролируемый под капотом (отсюда скорость), с подпиской на нужные поля.
Частая ошибка
Смешение режимов: value={maybeUndefined} — при undefined инпут становится неуправляемым, а при появлении значения React ругается «changing an uncontrolled input to be controlled». Значение должно быть всегда строкой (value={text ?? ''}).
Коротко для собеседования
Контролируемый: значение в state, value+onChange, живая валидация, перерендер на каждый ввод. Неконтролируемый: значение в DOM, defaultValue+ref, быстрее и проще, но без живого контроля. RHF строит формы на неконтролируемых полях, Formik — на контролируемых; главное — не смешивать режимы в одном поле.