Композиция и переиспользование

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

Композиция в Radix UI достигается через следующие подходы:

  1. Контейнерные компоненты – предоставляют контекст и управление состоянием, который используется дочерними элементами.
  2. Слот-компоненты (Slot) – позволяют внедрять внешние элементы в заранее определённые места компонента, сохраняя все обработчики событий и управление фокусом.
  3. Прозрачные компоненты (asChild) – позволяют заменять корневой элемент компонента на любой другой, сохраняя при этом поведение Radix UI.

Переиспользование состояния через контексты

Radix UI активно использует контекст React для управления состоянием компонента. Пример: компонент Dialog содержит состояние открытости, которое доступно всем вложенным элементам через контекст:

import * as Dialog from '@radix-ui/react-dialog';

function MyDialog() {
  return (
    
      Открыть диалог
      
        
        
          Заголовок
          Описание
          Закрыть
        
      
    
  );
}

В этом примере Dialog.Trigger и Dialog.Close используют один и тот же контекст, что упрощает управление состоянием и предотвращает дублирование логики.


Использование asChild для гибкой интеграции

Свойство asChild позволяет заменить корневой элемент Radix компонента на произвольный JSX элемент, сохраняя при этом все внутренние обработчики и управление фокусом:

import { Button } from '@radix-ui/react-button';
import * as Dialog from '@radix-ui/react-dialog';


  

Здесь Dialog.Trigger становится оберткой для кастомной кнопки, а все события клика и управления фокусом остаются корректными.


Создание переиспользуемых UI-блоков

Переиспользование в Radix UI строится на разделении функциональности и визуальной части:

  1. Функциональный компонент – управляет состоянием, событиями и логикой.
  2. Презентационный компонент – отвечает за стили и отображение.

Пример переиспользуемого выпадающего меню:

import * as DropdownMenu from '@radix-ui/react-dropdown-menu';

function CustomDropdown({ triggerLabel, items }) {
  return (
    
      {triggerLabel}
      
        {items.map((item) => (
          {item}
        ))}
      
    
  );
}

Такой компонент можно использовать в разных частях приложения, передавая разные наборы элементов через пропсы.


Слоты и композиция визуальных элементов

Slot позволяет внедрять внешние компоненты внутрь Radix компонента, не теряя управляемости и фокусировки:

import { Slot } from '@radix-ui/react-slot';
import * as Tooltip from '@radix-ui/react-tooltip';

function CustomTooltip({ children, content }) {
  return (
    
      
        {children}
      
      {content}
    
  );
}

Slot делает компонент максимально гибким, позволяя использовать любые элементы как триггеры или части интерфейса.


Рекомендации по архитектуре

  • Разделять логические компоненты и визуальные слои для максимальной переиспользуемости.
  • Использовать asChild и Slot для интеграции Radix UI с кастомной библиотекой стилей или компонентами.
  • Объединять несколько Radix компонентов через контейнерные компоненты для создания сложных интерфейсов.
  • Минимизировать дублирование логики состояния, используя контексты и композицию, а не внутренние локальные состояния каждого компонента.

Пример сложной композиции

Комбинация DropdownMenu и Dialog через asChild и контексты:

function ComplexMenu() {
  return (
    
      Меню
      
        
          
            
              
            
            
              
              
                Диалог внутри меню
              
            
          
        
      
    
  );
}

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