Compound components

В библиотеке MUI (Material-UI) концепция compound components позволяет создавать более гибкие и настраиваемые компоненты, объединяя несколько связанных элементов в один интерфейс. Основная идея заключается в разделении логики и разметки: один компонент управляет состоянием, а вложенные компоненты отвечают за визуализацию и отдельные части интерфейса.

Примером может служить компонент Tabs. Он содержит контейнер Tabs для управления состоянием активной вкладки и дочерние компоненты Tab для отображения отдельных элементов интерфейса.

import Tabs from '@mui/material/Tabs';
import Tab from '@mui/material/Tab';
import Box from '@mui/material/Box';
import React, { useState } from 'react';

function BasicTabs() {
  const [value, setValue] = useState(0);

  const handleChange = (event, newValue) => {
    setValue(newValue);
  };

  return (
    <Box sx={{ width: '100%' }}>
      <Tabs value={value} onCha nge={handleChange}>
        <Tab label="Вкладка 1" />
        <Tab label="Вкладка 2" />
        <Tab label="Вкладка 3" />
      </Tabs>
      <Box sx={{ p: 2 }}>
        {value === 0 && <div>Содержимое вкладки 1</div>}
        {value === 1 && <div>Содержимое вкладки 2</div>}
        {value === 2 && <div>Содержимое вкладки 3</div>}
      </Box>
    </Box>
  );
}

Здесь Tabs выступает в роли управляющего компонента, а Tab — дочернего, отвечающего за отдельные элементы интерфейса. Такой подход позволяет сохранять согласованное состояние и минимизировать повторение кода.


Принципы построения compound components

  1. Разделение ответственности Управляющий компонент отвечает за состояние и логику, а дочерние компоненты — за визуализацию. Например, компонент Accordion управляет открытием и закрытием панели, а AccordionSummary и AccordionDetails отображают заголовок и содержимое соответственно.

  2. Использование контекста Для передачи состояния от родителя к дочерним элементам MUI активно использует React Context. Это позволяет дочерним компонентам получать данные без необходимости пробрасывать пропсы через каждый уровень иерархии.

import React, { createContext, useContext, useState } from 'react';

const AccordionContext = createContext();

function Accordion({ children }) {
  const [expanded, setExpanded] = useState(false);
  const toggle = () => setExpanded(prev => !prev);

  return (
    <AccordionContext.Provider value={{ expanded, toggle }}>
      <div>{children}</div>
    </AccordionContext.Provider>
  );
}

function AccordionSummary({ children }) {
  const { toggle } = useContext(AccordionContext);
  return <div onCl ick={toggle}>{children}</div>;
}

function AccordionDetails({ children }) {
  const { expanded } = useContext(AccordionContext);
  return expanded ? <div>{children}</div> : null;
}
  1. Согласованная работа с пропсами Дочерние компоненты часто используют специальные пропсы, переданные управляющим компонентом. В MUI это реализовано через slot и slotProps, которые позволяют заменять отдельные элементы и настраивать их свойства.
<Tabs
  value={value}
  onCha nge={handleChange}
  slotProps={{
    tab: { sx: { textTransform: 'none', fontWeight: 'bold' } }
  }}
>
  <Tab label="Первая вкладка" />
</Tabs>

Преимущества подхода

  • Повторное использование логики Один управляющий компонент может работать с различными визуальными реализациями дочерних элементов, что повышает гибкость и уменьшает дублирование кода.

  • Согласованное состояние Compound components позволяют централизованно управлять состоянием, предотвращая рассинхронизацию элементов интерфейса.

  • Легкая кастомизация Через slot, slotProps и sx можно менять стили и структуру без изменения логики компонента.


Продвинутые возможности

  1. Составные элементы с динамическими пропсами Использование функций для slotProps позволяет динамически изменять свойства дочерних компонентов в зависимости от состояния.
<Tabs
  value={value}
  onCha nge={handleChange}
  slotProps={{
    tab: (tabIndex) => ({
      sx: { color: value === tabIndex ? 'primary.main' : 'text.secondary' }
    })
  }}
>
  <Tab label="Вкладка 1" />
  <Tab label="Вкладка 2" />
</Tabs>
  1. Контроль рендеринга дочерних компонентов Управляющий компонент может решать, какие элементы отображать, в зависимости от состояния или внешних условий. Это позволяет создавать адаптивные и интерактивные интерфейсы.

  2. Композиция с другими компонентами MUI Compound components легко интегрируются с другими элементами MUI, такими как Box, Grid или Paper, что упрощает построение сложных интерфейсов.


Практическая рекомендация

Для создания собственных compound components рекомендуется:

  • Использовать React Context для передачи состояния и методов.
  • Разделять визуальные элементы и логику управления.
  • Предусматривать возможность кастомизации через пропсы и слоты.
  • Минимизировать прямую передачу состояния через prop drilling, заменяя её контекстом.

Такой подход обеспечивает гибкость, согласованность и масштабируемость интерфейсов, полностью соответствуя философии MUI — предоставлять мощные и настраиваемые компоненты без избыточной сложности.