Переиспользуемые паттерны

React Aria предоставляет набор хуков, которые реализуют доступность интерфейсов (accessibility) на уровне компонентов, оставляя контроль над визуальной частью разработчику. Ключевая цель переиспользуемых паттернов — создание универсальных, модульных и доступных компонентов, которые можно использовать в разных частях приложения без дублирования кода.


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

Большинство паттернов React Aria строятся вокруг хуков, которые разделяют логику и представление:

  1. useButton — управляет поведением кнопки, включая фокус, нажатие и события клавиатуры.
  2. useCheckbox — обеспечивает синхронизацию состояния с ARIA-атрибутами, правильное управление фокусом и поддержку клавиатуры.
  3. useMenu, useMenuItem — реализуют навигацию по меню с клавиатуры и доступ для скринридеров.

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

function useCustomToggle(initialState = false) {
  const [isOpen, setIsOpen] = React.useState(initialState);
  const toggle = () => setIsOpen(prev => !prev);

  return {
    isOpen,
    toggle,
    props: {
      'aria-expanded': isOpen,
      onClick: toggle
    }
  };
}

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


Комбинирование хуков для сложных компонентов

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

import { useSelect, useButton } from '@react-aria/sel ect';
import { useState, useRef } fr om 'react';

function CustomSelect({ options }) {
  const [selectedKey, setSelectedKey] = useState(null);
  const ref = useRef();
  const { labelProps, triggerProps, valueProps, menuProps } = useSelect(
    { selectedKey, onSelectionChange: setSelectedKey, items: options },
    ref
  );
  const buttonProps = useButton(triggerProps, ref);

  return (
    <div>
      <label {...labelProps}>Выберите элемент</label>
      <button {...buttonProps} ref={ref}>
        <span {...valueProps}>{selectedKey ?? "Выберите"}</span>
      </button>
      <ul {...menuProps}>
        {options.map(item => (
          <li key={item.key}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

Паттерн переиспользования: логика выбора и состояние хука отделены от рендеринга списка и кнопки, что позволяет использовать тот же useSelect для других визуальных реализаций (дропдауны, кастомные панели).


Декомпозиция компонентов

Ключевой принцип — разделение визуального компонента и логики доступности. Например:

  • ToggleSwitch рендерит визуализацию переключателя.
  • useToggle управляет состоянием и ARIA-атрибутами.
function ToggleSwitch({ isOn, onChange }) {
  const { inputProps } = useToggle({ isSelected: isOn, onChange });
  return <input type="checkbox" {...inputProps} />;
}

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

  • Компонент становится полностью тестируемым, так как состояние можно мокать через хук.
  • Легко интегрируется в разные UI-библиотеки.
  • Обеспечивается универсальная доступность без дублирования ARIA-логики.

Реиспользуемые паттерны для навигации

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

  1. useListBox + useOption Обеспечивает поддержку клавиатурного навигационного фокуса.
  2. useTable + useGridRow, useGridCell Позволяет создавать таблицы с ARIA-состояниями для скринридеров.
  3. useFocusRing Универсальный паттерн для отображения фокусного кольца без нарушения визуальной стилистики.

Контроль состояния через контекст

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

const MenuContext = React.createContext();

function MenuProvider({ children }) {
  const state = useMenuState(); // кастомный хук для состояния меню
  return <MenuContext.Provider value={state}>{children}</MenuContext.Provider>;
}

function MenuItem({ item }) {
  const state = React.useContext(MenuContext);
  const { menuItemProps } = useMenuItem({ key: item.key }, state);
  return <li {...menuItemProps}>{item.name}</li>;
}

Паттерн переиспользования: любой компонент внутри провайдера автоматически получает доступ к состоянию меню без дублирования логики.


Универсальные хелперы для ARIA

В дополнение к хукам, React Aria предоставляет хелперы, которые часто переиспользуются:

  • mergeProps — объединяет несколько наборов пропсов, чтобы не потерять события и ARIA-атрибуты.
  • filterDOMProps — фильтрует допустимые HTML-атрибуты, предотвращая ошибки при передаче неизвестных свойств.
import { mergeProps } from '@react-aria/utils';

function CustomInput({ inputProps, additionalProps }) {
  const props = mergeProps(inputProps, additionalProps);
  return <input {...props} />;
}

Выводы по паттернам

  • Логика и визуальная часть разделены через хуки и компоненты.
  • Состояние контролируется хуками или контекстом, что упрощает переиспользование.
  • ARIA-атрибуты и фокус управляются автоматически, минимизируя ошибки доступности.
  • Хелперы (mergeProps, filterDOMProps) делают компоненты более универсальными.
  • Композиция хуков позволяет строить сложные интерфейсы, сохраняя чистую архитектуру.

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