Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що робить патерн "compound components" з контекстом?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Compound Components (складені компоненти)** - це патерн, за якого кілька пов'язаних компонентів працюють разом, як єдиний "компонент-група", спілкуючись між собою через React Context замість передачі пропсів вручну. **Ключове:** батьківський компонент керує станом і надає його через контекст, а дочірні компоненти просто підписуються на цей контекст, тому prop drilling зникає повністю.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Що таке "Compound Components" **Compound Components (складені компоненти)** - це патерн, за якого **кілька пов'язаних компонентів** працюють **разом**, як єдиний "компонент-група". Зазвичай у нас є "батько" та "дочірні елементи", які **спілкуються між собою через React Context**, замість передачі пропсів вручну. --- ## Приклад (інтуїтивно) ### Без патерна ```javascript <Tabs> <TabList> <Tab label="Опис" activeTab={activeTab} setActiveTab={setActiveTab} /> <Tab label="Відгуки" activeTab={activeTab} setActiveTab={setActiveTab} /> </TabList> <TabPanels activeTab={activeTab}> <TabPanel index={0}>Контент 1</TabPanel> <TabPanel index={1}>Контент 2</TabPanel> </TabPanels> </Tabs> ``` Мінуси: - доводиться прокидувати купу пропсів (`activeTab`, `setActiveTab`); - компоненти "жорстко зв'язані" - використовувати їх окремо незручно. --- ## З патерном Compound Components ### Мета: - Щоб `Tabs`, `TabList`, `Tab`, `TabPanel` **самі знали про свій стан** через **внутрішній контекст**; - щоб код виглядав природно: ```javascript <Tabs> <Tabs.List> <Tabs.Tab>Опис</Tabs.Tab> <Tabs.Tab>Відгуки</Tabs.Tab> </Tabs.List> <Tabs.Panels> <Tabs.Panel>Контент 1</Tabs.Panel> <Tabs.Panel>Контент 2</Tabs.Panel> </Tabs.Panels> </Tabs> ``` --- ## Як це працює ### 1. Створюємо контекст ```javascript const TabsContext = createContext(null); ``` ### 2. Батьківський компонент (Provider) ```javascript function Tabs({ children }) { const [activeIndex, setActiveIndex] = useState(0); const value = useMemo(() => ({ activeIndex, setActiveIndex }), [activeIndex]); return ( <TabsContext.Provider value={value}> <div className="tabs">{children}</div> </TabsContext.Provider> ); } ``` ### 3. Дочірні компоненти - підписники контексту ```javascript function TabList({ children }) { return <div className="tab-list">{children}</div>; } function Tab({ children, index }) { const { activeIndex, setActiveIndex } = useContext(TabsContext); const isActive = index === activeIndex; return ( <button className={isActive ? "active" : ""} onClick={() => setActiveIndex(index)} > {children} </button> ); } function Panels({ children }) { return <div className="tab-panels">{children}</div>; } function Panel({ children, index }) { const { activeIndex } = useContext(TabsContext); return activeIndex === index ? <div>{children}</div> : null; } ``` ### 4. Збираємо "складений" компонент ```javascript Tabs.List = TabList; Tabs.Tab = Tab; Tabs.Panels = Panels; Tabs.Panel = Panel; ``` --- ## Що робить контекст тут Контекст використовується як **внутрішній механізм зв'язку** між компонентами, щоб вони могли **"знати" стан батька (Tabs)**, **не потребуючи props** на кожному рівні. Тобто: - `Tabs` керує станом (активна вкладка); - `Tabs.Tab` і `Tabs.Panel` просто "підписані" на цей контекст; - усе синхронізується автоматично. --- ## Переваги патерна | Перевага | Опис | |---|---| | Чистий API | Вкладений JSX відображає ієрархію логіки | | Повторне використання | Компоненти можна перевикористовувати незалежно | | Інкапсуляція | Стан Tabs прихований, доступ є тільки через контекст | | Гнучкість | Можна вставляти власні компоненти між ними | | Немає prop drilling | Жодні пропси вручну не передаються вниз | --- ## Де це використовується Цей патерн використовується повсюди: - у **Headless UI**, **Radix UI**, **shadcn/ui**, **React Aria**; - для **Tabs**, **Accordion**, **Dropdown**, **Menu**, **Modal**, **Form**, **Stepper**. --- ## Підсумок **Compound Components + Context =** > гнучкий інтерфейс, де дочірні компоненти автоматично знають, > у якому контексті вони перебувають, і синхронізуються без пропсів. --- ## Формула патерна: 1. Створюєш контекст всередині "батька"; 2. Провайдиш значення через `<Provider>`; 3. Дочірні компоненти дістають стан із контексту; 4. Експортуєш дочірні компоненти як властивості головного.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.