Составные компоненты
— 3 минуты
Есть такой паттерн для реакта, который называется Compound Components. Это можно перевести как "составные компоненты"
Смысл этого паттерна заключается в том, что мы можем связать компоненты общим окружением и эффективнее использовать их вместе
То есть мы можем заранее объединить компоненты каким-то контекстом и переиспользовать их, например, через общее пространство имён, в качестве которого чаще всего выступает родительский компонент
Запутались в словах? Лучше посмотреть в коде:
Вот пример прям из доки Ant Design:
javascript1import { Layout } from 'antd'; 2 3<Layout> 4 <Layout.Header>Header</Layout.Header> 5 <Layout.Content>Content</Layout.Content> 6 <Layout.Footer>Footer</Layout.Footer> 7</Layout> 8
Обратили внимание, как компоненты Header, Content и Footer мы получаем напрямую из компонента Layout? Это и есть пример паттерна Compound Components. Компоненты связаны, а используются они из общего пространства — компонента Layout
Зачем же так сделали? Тут преследуется три цели:
- Явно показать на уровне нейминга, что использовать Layout.Footer вне Layout не нужно
- Расшарить общий контекст между всеми компонентами Layout
- Корректно стилизовать части Layout в зависимости от значения внутри общего контекста
С неймингом и стилями, думаю, всё предельно ясно. Но что насчёт контекста? На самом деле, Layout под собой содержит ещё и LayoutContext, который содержит в себе состояние компонента Sider и распространяет его на все дочерние компоненты. Схематически это выглядит примерно так:
text1InternalLayout 2└ LayoutContext <-- инициализируем контекст 3 ├ Header 4 ├ Content <-- а в этих компонентах получаем его значение 5 ├ Footer 6 └ Sider 7
В итоге получится, что все дочерние компоненты, пытающиеся получить доступ к контексту, без обёртки работать будут криво или и вовсе не будут
С точки зрения типов всё тоже не сложно. Тот же Ant Design делает так:
typescript1import InternalLayout, { Content, Footer, Header } from './layout'; 2import Sider from './Sider'; 3 4// получаем тип базового layout компонента 5type InternalLayoutType = typeof InternalLayout; 6 7// создаём тип, который определит какие компоненты мы вложим в layout 8type CompoundedComponent = InternalLayoutType & { 9 Header: typeof Header; 10 Footer: typeof Footer; 11 Content: typeof Content; 12 Sider: typeof Sider; 13}; 14 15// нагло переприсваиваем тип 16const Layout = InternalLayout as CompoundedComponent; 17 18// нагло биндим нужные компоненты 19Layout.Header = Header; 20Layout.Footer = Footer; 21Layout.Content = Content; 22Layout.Sider = Sider; 23 24// не менее нагло экспортируем как public-api 25export default Layout; 26
Статья была полезной?
Читайте также:
— 2 минуты
Как реагировать на изменения объекта
В JavaScript обычные объекты не умеют уведомлять о своих изменениях, однако...
— 4 минуты
Связываем React и localStorage через useSyncExternalStore
Как согласовать изменение состояния в реакте и поля в localStorage? До нед...
— 2 минуты
Как менять состояние вкладки по интервалу
Полезная фича для демонстрации, например, уведомлений, которая может пригод...