Skip to main content

Що робить патерн «Control Props»?

Control Props pattern - це один із найцікавіших і найпотужніших архітектурних прийомів у React, який дозволяє зробити компоненти як «контрольованими», так і «неконтрольованими» - за вибором користувача.

Цей патерн лежить в основі багатьох бібліотек (наприклад, React Select, Downshift, Headless UI, Radix UI та ін.), тому що він дає гнучкість керування станом компонента.

Розберемося покроково.


Визначення

Control Props Pattern - це патерн, за якого компонент може працювати у двох режимах:

  • Неконтрольований (внутрішній) - керує своїм станом сам
  • Контрольований (зовнішній) - приймає стан і колбеки через props, віддаючи керування назовні

Іншими словами:

Компонент віддає «важелі керування» користувачу, але якщо користувач їх не візьме, компонент працює сам.


Приклад: перемикач (Toggle)

Без патерна (лише внутрішній state)

javascript
function Toggle() { const [on, setOn] = useState(false); return ( <button onClick={() => setOn(!on)}> {on ? "ON" : "OFF"} </button> ); }

Компонент працює, але він завжди керує собою сам. Ми не можемо контролювати стан on ззовні.


З Control Props

javascript
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)

javascript
<Toggle />

Компонент зберігає стан сам.


Керований ззовні (controlled)

javascript
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

javascript
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} />; }

Використання:

javascript
// Uncontrolled <Input defaultValue="Hello" /> // Controlled const [name, setName] = useState(""); <Input value={name} onChange={setName} />

Один компонент - два режими роботи. Це і є суть Control Props Pattern.


Важливі принципи

  1. Визнач, які пропси можуть бути «контрольованими» Наприклад, value, checked, open, selected, expanded тощо.
  2. Додай callback для сповіщення Наприклад, onChange, onToggle, onSelect.
  3. Внутрішній стан має синхронізуватися із зовнішнім Якщо проп приходить, використовуємо його. Якщо ні, власний useState.
  4. Будь передбачуваним Компонент не повинен раптово змінювати поведінку між «controlled» і «uncontrolled» протягом свого життя.

Контроль через «control prop»

javascript
<Toggle on={true} /> // fully controlled by the parent <Toggle /> // managed internally <Toggle onChange={handler} /> // works in both scenarios

Де використовується цей патерн

БібліотекаКомпонентControl Prop
React, value / onChange
React-Selectvalue / onChange
DownshiftisOpen, selectedItem, inputValue
Radix UI / Headless UI, open / onOpenChange

Практично кожен UI-компонент «стану» реалізований через Control Props Pattern.


Підсумок

ЩоОпис
ІдеяДозволити компоненту бути «контрольованим» або «неконтрольованим»
Як працюєЯкщо prop (наприклад value або on) передано, керування переходить до батьківського компонента
МетаГнучкість: компонент працює і сам по собі, і у складі складних систем
Приклад
Де використовуєтьсяInputs, Dialogs, Dropdowns, Comboboxes, Checkboxes
АналогіяКомпонент каже: «Ти можеш керувати мною, а можеш дозволити мені керувати собою».

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.