Создание составных компонентов

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

Контейнерные и дочерние компоненты

Составной компонент обычно строится вокруг контейнерного компонента, который управляет состоянием и передает данные своим дочерним элементам через props или React Context. Это позволяет:

  • централизовать логику управления,
  • минимизировать дублирование кода,
  • создавать гибкие интерфейсы с согласованной поведением.

Пример контейнера с передачей состояния дочерним элементам через props:

import { Box, Button, Text } from "@chakra-ui/react";
import { useState } from "react";

function Toggle({ children }) {
  const [on, setOn] = useState(false);

  const toggle = () => setOn(!on);

  return children({ on, toggle });
}

export default function Example() {
  return (
    <Toggle>
      {({ on, toggle }) => (
        <Box>
          <Button onCl ick={toggle}>Переключить</Button>
          <Text>{on ? "Включено" : "Выключено"}</Text>
        </Box>
      )}
    </Toggle>
  );
}

Здесь Toggle выступает как контейнер, а дочерние элементы получают управляемое состояние через функцию.

Использование контекста для вложенных компонентов

Для более сложных составных компонентов часто используют React.createContext. Chakra UI отлично интегрируется с этим подходом, обеспечивая чистый и декларативный API.

Пример создания контекста:

import { createContext, useContext, useState } from "react";
import { Box, Button } from "@chakra-ui/react";

const AccordionContext = createContext();

function Accordion({ children }) {
  const [openIndex, setOpenIndex] = useState(null);
  return (
    <AccordionContext.Provider value={{ openIndex, setOpenIndex }}>
      <Box>{children}</Box>
    </AccordionContext.Provider>
  );
}

function AccordionItem({ index, title, children }) {
  const { openIndex, setOpenIndex } = useContext(AccordionContext);
  const isOpen = openIndex === index;

  return (
    <Box borderWidth="1px" borderRadius="md" mb={2}>
      <Button width="100%" onCl ick={() => setOpenIndex(isOpen ? null : index)}>
        {title}
      </Button>
      {isOpen && <Box p={4}>{children}</Box>}
    </Box>
  );
}

export default function App() {
  return (
    <Accordion>
      <AccordionItem index={0} title="Первый элемент">
        Содержимое первого элемента
      </AccordionItem>
      <AccordionItem index={1} title="Второй элемент">
        Содержимое второго элемента
      </AccordionItem>
    </Accordion>
  );
}

Этот подход позволяет изолировать состояние каждого элемента и при этом управлять всей группой через контекст.

Использование forwardRef для совместимости с Chakra UI

Для создания более интегрированных составных компонентов важно поддерживать передачу ref. Chakra UI активно использует ref для управления фокусом, анимациями и доступностью.

Пример с forwardRef:

import { forwardRef } from "react";
import { Box } from "@chakra-ui/react";

const Card = forwardRef(({ children, ...props }, ref) => (
  <Box ref={ref} borderWidth="1px" borderRadius="md" p={4} {...props}>
    {children}
  </Box>
));

export default Card;

Использование forwardRef позволяет родительским компонентам обращаться к DOM-элементам дочернего компонента напрямую, что особенно полезно для интерактивных виджетов.

Стилизация через Chakra Props и useStyleConfig

Составные компоненты легко настраиваются с помощью системы стилей Chakra UI. Для переиспользуемости рекомендуется использовать useStyleConfig или chakra для обертки базовых элементов.

Пример:

import { Box, useStyleConfig } from "@chakra-ui/react";

function CustomButton(props) {
  const styles = useStyleConfig("Button", props);
  return <Box as="button" sx={styles} {...props} />;
}

Это позволяет создавать единый стиль компонентов в соответствии с дизайн-системой, сохраняя гибкость для специфических вариантов.

Передача событий и коллбеков

Составные компоненты часто требуют передачи коллбеков вниз по иерархии или получения уведомлений от дочерних компонентов. Для этого применяются props типа onChange, onSelect, onToggle, что позволяет централизованно обрабатывать события.

function CheckboxGroup({ onChange, children }) {
  const [selected, setSelected] = useState([]);

  const toggleItem = (value) => {
    const newSelected = selected.includes(value)
      ? selected.filter((v) => v !== value)
      : [...selected, value];
    setSelected(newSelected);
    onChange?.(newSelected);
  };

  return children({ selected, toggleItem });
}

Этот подход делает компоненты модульными и независимыми, но при этом легко управляемыми.

Интеграция с анимациями и переходами

Chakra UI предоставляет Collapse, Fade, ScaleFade, Slide, которые отлично сочетаются с составными компонентами для создания интерактивных и плавных интерфейсов.

Пример:

import { Collapse, Button, Box } from "@chakra-ui/react";
import { useState } from "react";

function AnimatedPanel() {
  const [show, setShow] = useState(false);
  return (
    <Box>
      <Button onCl ick={() => setShow(!show)}>Переключить</Button>
      <Collapse in={show} animateOpacity>
        <Box p={4} mt={2} bg="gray.100" rounded="md">
          Анимированное содержимое
        </Box>
      </Collapse>
    </Box>
  );
}

Комбинация составных компонентов и анимаций позволяет создавать продвинутые интерфейсы без нарушения принципов переиспользуемости и управления состоянием.

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

  • Разделять логический слой и визуальный слой компонентов. Контейнеры управляют состоянием, дочерние элементы — отображают интерфейс.
  • Использовать Context API для управления глобальными состояниями составных компонентов.
  • Поддерживать передачу ref через forwardRef для совместимости с Chakra UI.
  • Стандартизировать стили с помощью useStyleConfig для согласованности визуального языка.
  • Сохранять API компонентов простым, используя props и коллбеки, избегая глубоких вложенных структур, которые сложно поддерживать.

Создание составных компонентов в Chakra UI — это сочетание декларативного React-подхода, гибкой системы стилей и удобной работы с состоянием, что позволяет строить интерфейсы любого уровня сложности, оставаясь при этом модульными и поддерживаемыми.