Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що робить патерн «Control Props»?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Control Props pattern** - це архітектурний прийом у React, який дозволяє зробити компоненти як «контрольованими», так і «неконтрольованими» - за вибором користувача. **Ключове:** компонент віддає «важелі керування» користувачу, але якщо користувач їх не візьме, компонент працює сам.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**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 він все одно працює сам | --- ## Ще один приклад: <Input> із 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 | <input>, <textarea> | value / onChange | | React-Select | <Select> | value / onChange | | Downshift | <Combobox> | isOpen, selectedItem, inputValue | | Radix UI / Headless UI | <Dialog>, <Popover> | open / onOpenChange | Практично кожен UI-компонент «стану» реалізований через Control Props Pattern. --- ## Підсумок | Що | Опис | |---|---| | Ідея | Дозволити компоненту бути «контрольованим» або «неконтрольованим» | | Як працює | Якщо prop (наприклад value або on) передано, керування переходить до батьківського компонента | | Мета | Гнучкість: компонент працює і сам по собі, і у складі складних систем | | Приклад | <Toggle on={...} onChange={...} /> | | Де використовується | Inputs, Dialogs, Dropdowns, Comboboxes, Checkboxes | | Аналогія | Компонент каже: «Ти можеш керувати мною, а можеш дозволити мені керувати собою». |Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.