Составные компоненты с Context
Передавайте неявное состояние между родительским компонентом и его назначенными дочерними компонентами с помощью Context, моделируя API вроде и
«Составные компоненты с Context» — бесплатный урок Frontend Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Frontend Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Frontend Academy содержит 4 уроков всего.
Что такое составные компоненты
Составные компоненты — это набор связанных компонентов, предназначенных для совместной работы как единое целое, например HTML-элементы <select> и <option>. Родительский компонент передаёт дочерним компонентам неявное состояние.
Цель: более чистые API
Вместо <Select options={[...]} value={x} onChange={...} /> Вы пишете <Select><Option value="a">Apple</Option></Select>. Такая структура декларативна и естественно использует JSX.
Наивный подход: проблема
Передача состояния через свойства требует от пользователя связать всё вручную. Дочерние компоненты не получают сведения о состоянии родителя автоматически.
// Without compound pattern — verbose:
<Tabs activeIndex={0} onChange={setIndex}>
<TabList>
<Tab index={0}>Home</Tab>
<Tab index={1}>Profile</Tab>
</TabList>
</Tabs>Контекст как связующее звено
Родительский компонент создаёт Context с общим состоянием и предоставляет его. Дочерние компоненты используют контекст — явная передача свойств не требуется.
import { createContext, useContext, useState } from 'react';
const TabsContext = createContext(null);
export function Tabs({ children, defaultIndex = 0 }) {
const [activeIndex, setActiveIndex] = useState(defaultIndex);
return (
<TabsContext.Provider value={{ activeIndex, setActiveIndex }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}Дочерние компоненты используют контекст
Каждый дочерний компонент читает данные из контекста — свойства не приходится передавать через всё дерево.
export function Tab({ index, children }) {
const { activeIndex, setActiveIndex } = useContext(TabsContext);
const isActive = activeIndex === index;
return (
<button
className={isActive ? 'active' : ''}
onClick={() => setActiveIndex(index)}
>
{children}
</button>
);
}
export function Panel({ index, children }) {
const { activeIndex } = useContext(TabsContext);
if (activeIndex !== index) return null;
return <div className="panel">{children}</div>;
}Статические свойства для удобного поиска
Присоединяйте вложенные компоненты к родительскому, чтобы пользователи находили их с помощью автодополнения.
Tabs.Tab = Tab;
Tabs.Panel = Panel;
// Usage:
<Tabs>
<Tabs.Tab index={0}>Home</Tabs.Tab>
<Tabs.Tab index={1}>Profile</Tabs.Tab>
<Tabs.Panel index={0}>Home content</Tabs.Panel>
<Tabs.Panel index={1}>Profile content</Tabs.Panel>
</Tabs>Практический пример: Disclosure
Виджет Disclosure содержит дочерние компоненты Trigger и Panel, которые совместно используют через контекст состояние открытия и закрытия.
function Disclosure({ children }) {
const [isOpen, setIsOpen] = useState(false);
return (
<DisclosureContext.Provider value={{ isOpen, setIsOpen }}>
<div>{children}</div>
</DisclosureContext.Provider>
);
}
function Trigger({ children }) {
const { isOpen, setIsOpen } = useContext(DisclosureContext);
return <button onClick={() => setIsOpen(o => !o)}>{children}</button>;
}
function Panel({ children }) {
const { isOpen } = useContext(DisclosureContext);
return isOpen ? <div>{children}</div> : null;
}Хуки как публичный API
Экспортируйте пользовательский хук, чтобы пользователи могли создавать собственные компоненты поверх Ваших.
export function useTabs() {
const ctx = useContext(TabsContext);
if (!ctx) throw new Error('useTabs must be used inside <Tabs>');
return ctx;
}Полиморфные составные компоненты
Позволяйте родительским компонентам отрисовывать любой элемент с помощью свойства as или шаблона asChild (подход Radix UI). Это позволяет составным компонентам сочетаться с произвольным JSX.
Составные компоненты без представления
Библиотеки без представления (Radix, Headless UI) поставляют составные компоненты без стилей: логика доступности уже встроена, а стили Вы задаёте самостоятельно.
Проблема: анализ дочерних компонентов
В старом подходе использовался React.Children для анализа дочерних компонентов — это хрупкое и ненадёжное решение. Для совместного использования состояния всегда предпочитайте контекст.
Быстрая проверка
Как дочерние компоненты взаимодействуют с родительским в шаблоне составных компонентов?
Итоги: составные компоненты
Составные компоненты позволяют связанным частям работать как единое декларативное целое. Используйте Context для общего состояния: Provider в родительском компоненте, useContext в дочерних. Присоединяйте вложенные компоненты как статические свойства, чтобы их было легко находить. Экспортируйте пользовательский хук как публичный API. Radix и Headless UI — отличные источники примеров. Предпочитайте контекст анализу Children.
Часто задаваемые вопросы
Урок «Составные компоненты с Context» бесплатный?
Да — полный текст урока «Составные компоненты с Context» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Frontend Academy, подпишись на CoddyKit PRO. Курс Frontend Academy содержит 4 уроков всего.
Чему я научусь в уроке «Составные компоненты с Context»?
Передавайте неявное состояние между родительским компонентом и его назначенными дочерними компонентами с помощью Context, моделируя API вроде и Ты практикуешь Frontend Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Frontend Academy?
Предыдущий опыт не требуется. Frontend Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Составные компоненты с Context»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Frontend Academy?
Да. Каждый урок Frontend Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Составные компоненты с Context
- Свойства рендеринга и шаблоны HOC
- Порталы для модальных окон и подсказок
- Границы ошибок