Паттерны композиции

React Aria предоставляет набор хуков, которые обеспечивают доступность интерфейсов без привязки к конкретным визуальным компонентам. Центральным подходом является разделение логики состояния и представления. Вместо того чтобы создавать готовые визуальные компоненты, React Aria предоставляет паттерны композиции, которые позволяют комбинировать поведение и внешний вид по необходимости.

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

Каждый хук в React Aria возвращает объект с свойствами и обработчиками событий, которые можно безопасно применять к любым элементам DOM или кастомным компонентам. Например, useButton не создаёт кнопку визуально, но возвращает необходимые атрибуты и события для корректной работы:

import {useButton} from '@react-aria/button';
import {useRef} from 'react';

function CustomButton({onPress, children}) {
  let ref = useRef();
  let {buttonProps} = useButton({onPress}, ref);

  return (
    <button {...buttonProps} ref={ref}>
      {children}
    </button>
  );
}

Ключевые моменты:

  • useButton добавляет правильные ARIA-атрибуты (aria-pressed, role="button" и др.).
  • Обработчики событий (onClick, onKeyDown) автоматически настроены под доступность.
  • Компонент можно стилизовать как угодно, не теряя функционала.

Паттерн “Render Props”

React Aria активно использует концепцию render props для передачи состояния и обработчиков в визуальные компоненты. Это позволяет полностью контролировать разметку и стили:

import {useToggleButton} from '@react-aria/button';

function Toggle({children}) {
  let ref = useRef();
  let {toggleButtonProps, isSelected} = useToggleButton({onPress: () => {}}, ref);

  return children({toggleButtonProps, isSelected, ref});
}

// Использование
<Toggle>
  {({toggleButtonProps, isSelected, ref}) => (
    <button {...toggleButtonProps} ref={ref}>
      {isSelected ? 'Вкл' : 'Выкл'}
    </button>
  )}
</Toggle>

Преимущества паттерна:

  • Разделение логики состояния (isSelected) и разметки.
  • Возможность создания сложных UI с собственными стилями.
  • Простое повторное использование логики в разных компонентах.

Паттерн “Hook Composition”

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

import {useMenu, useMenuItem} from '@react-aria/menu';
import {useListState} from '@react-stately/list';
import {useRef} from 'react';

function Menu({items}) {
  let state = useListState({items});
  let ref = useRef();
  let {menuProps} = useMenu({}, state, ref);

  return (
    <ul {...menuProps} ref={ref}>
      {[...state.collection].map(item => (
        <MenuItem key={item.key} item={item} state={state} />
      ))}
    </ul>
  );
}

function MenuItem({item, state}) {
  let ref = useRef();
  let {menuItemProps} = useMenuItem({key: item.key}, state, ref);

  return (
    <li {...menuItemProps} ref={ref}>
      {item.rendered}
    </li>
  );
}

Важные моменты:

  • useListState управляет состоянием списка (selectedKeys, focusedKey).
  • useMenu и useMenuItem добавляют доступность и управление событиями.
  • Хуки можно комбинировать для создания любой структуры без жёсткой привязки к DOM.

Паттерн “Component Slots”

React Aria поддерживает концепцию слотов, когда логика одного компонента делится между несколькими визуальными частями. Например, useSlider предоставляет свойства для трека, ползунка и меток:

import {useSlider} from '@react-aria/slider';
import {useSliderState} from '@react-stately/slider';
import {useRef} from 'react';

function Slider({value, onChange, min, max}) {
  let state = useSliderState({value, onChange, min, max});
  let ref = useRef();
  let {trackProps, thumbProps} = useSlider({min, max}, state, ref);

  return (
    <div {...trackProps} ref={ref} className="track">
      <div {...thumbProps} className="thumb" />
    </div>
  );
}

Преимущества:

  • Каждая часть слайдера получает правильные атрибуты.
  • Можно отдельно стилизовать трек и ползунок.
  • Состояние и взаимодействие остаются полностью управляемыми.

Контроль доступа и универсальность

Основная цель паттернов композиции — гарантировать доступность без наложения визуальных ограничений. Хуки React Aria заботятся о:

  • правильных ARIA-атрибутах,
  • управлении клавиатурой,
  • поддержке экранных читалок,
  • правильной фокусировке и навигации.

Практика повторного использования

Паттерны композиции позволяют создавать модульные компоненты, которые можно:

  • использовать в разных UI,
  • комбинировать для сложных виджетов,
  • настраивать стили без изменения логики поведения.

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

Итоговые принципы

  1. Разделение логики и визуализации через хуки и render props.
  2. Композиция хуков для сложных виджетов.
  3. Слоты и паттерн render props для гибкого управления разметкой.
  4. Повторное использование состояния с помощью useState, useListState, useSliderState.
  5. Фокус на доступности, предоставляемой React Aria автоматически.

Эти подходы формируют основу гибкого и масштабируемого интерфейсного кода на базе React Aria.