Що робить патерн «Control Props»?
Control Props pattern - це один із найцікавіших і найпотужніших архітектурних прийомів у React, який дозволяє зробити компоненти як «контрольованими», так і «неконтрольованими» - за вибором користувача.
Цей патерн лежить в основі багатьох бібліотек (наприклад, React Select, Downshift, Headless UI, Radix UI та ін.), тому що він дає гнучкість керування станом компонента.
Розберемося покроково.
Визначення
Control Props Pattern - це патерн, за якого компонент може працювати у двох режимах:
- Неконтрольований (внутрішній) - керує своїм станом сам
- Контрольований (зовнішній) - приймає стан і колбеки через props, віддаючи керування назовні
Іншими словами:
Компонент віддає «важелі керування» користувачу, але якщо користувач їх не візьме, компонент працює сам.
Приклад: перемикач (Toggle)
Без патерна (лише внутрішній state)
function Toggle() {
const [on, setOn] = useState(false);
return (
<button onClick={() => setOn(!on)}>
{on ? "ON" : "OFF"}
</button>
);
}Компонент працює, але він завжди керує собою сам. Ми не можемо контролювати стан on ззовні.
З Control Props
function Toggle({ on: controlledOn, onChange }) {
const [internalOn, setInternalOn] = useState(false);
// If the "on" prop is passed, use it; otherwise use internal state
const isControlled = controlledOn !== undefined;
const on = isControlled ? controlledOn : internalOn;
const toggle = () => {
const newState = !on;
if (!isControlled) setInternalOn(newState); // internal control
onChange?.(newState); // notify the outside world
};
return <button onClick={toggle}>{on ? "ON" : "OFF"}</button>;
}Використання
Керований зсередини (uncontrolled)
<Toggle />Компонент зберігає стан сам.
Керований ззовні (controlled)
function App() {
const [on, setOn] = useState(false);
return (
<Toggle on={on} onChange={setOn} />
);
}Тепер батьківський компонент контролює стан on, а Toggle просто «сигналізує» про зміни.
Що відбувається
| Варіант | Хто керує станом | Де зберігається значення |
|---|---|---|
| Неконтрольований | Компонент сам | У useState всередині |
| Контрольований | Батьківський компонент | У useState батьківського компонента |
| Обидва варіанти | Визначається автоматично за props | Залежно від наявності on |
Навіщо потрібен Control Props
| Проблема | Як вирішує |
|---|---|
| Хочеш компонент «з коробки», але з можливістю кастомізації | Дозволяє керувати станом ззовні, якщо потрібно |
| Потрібен компонент, який може бути «керованим» або «автономним» | Один і той самий код підтримує обидва режими |
| Хочеш вбудувати компонент у складну форму або стейт-менеджер | Передай йому value і onChange, і він буде «controlled» |
| Не хочеш втрачати зручність простого використання | Без props він все одно працює сам |
Ще один приклад: із Control Props
function Input({ value: controlledValue, onChange, defaultValue = "" }) {
const [internalValue, setInternalValue] = useState(defaultValue);
const isControlled = controlledValue !== undefined;
const value = isControlled ? controlledValue : internalValue;
const handleChange = (e) => {
if (!isControlled) setInternalValue(e.target.value);
onChange?.(e.target.value);
};
return <input value={value} onChange={handleChange} />;
}Використання:
// Uncontrolled
<Input defaultValue="Hello" />
// Controlled
const [name, setName] = useState("");
<Input value={name} onChange={setName} />Один компонент - два режими роботи. Це і є суть Control Props Pattern.
Важливі принципи
- Визнач, які пропси можуть бути «контрольованими» Наприклад, value, checked, open, selected, expanded тощо.
- Додай callback для сповіщення Наприклад, onChange, onToggle, onSelect.
- Внутрішній стан має синхронізуватися із зовнішнім Якщо проп приходить, використовуємо його. Якщо ні, власний useState.
- Будь передбачуваним Компонент не повинен раптово змінювати поведінку між «controlled» і «uncontrolled» протягом свого життя.
Контроль через «control prop»
<Toggle on={true} /> // fully controlled by the parent
<Toggle /> // managed internally
<Toggle onChange={handler} /> // works in both scenariosДе використовується цей патерн
| Бібліотека | Компонент | Control Prop |
|---|---|---|
| React | , | value / onChange |
| React-Select | value / onChange | |
| Downshift | isOpen, selectedItem, inputValue | |
| Radix UI / Headless UI | open / onOpenChange |
Практично кожен UI-компонент «стану» реалізований через Control Props Pattern.
Підсумок
| Що | Опис |
|---|---|
| Ідея | Дозволити компоненту бути «контрольованим» або «неконтрольованим» |
| Як працює | Якщо prop (наприклад value або on) передано, керування переходить до батьківського компонента |
| Мета | Гнучкість: компонент працює і сам по собі, і у складі складних систем |
| Приклад | |
| Де використовується | Inputs, Dialogs, Dropdowns, Comboboxes, Checkboxes |
| Аналогія | Компонент каже: «Ти можеш керувати мною, а можеш дозволити мені керувати собою». |
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.