Композиция хуков в библиотеке React Aria позволяет создавать сложные интерактивные компоненты, объединяя низкоуровневые хуки для управления состоянием, фокусом, клавиатурной навигацией и доступностью. React Aria не ограничивается только готовыми компонентами — её сила в возможности комбинировать хуки, создавая собственные, полностью контролируемые элементы интерфейса.
Композиция означает последовательное объединение нескольких хуков так, чтобы их состояние и поведение не конфликтовали друг с другом. Каждый хук отвечает за отдельный аспект компонента:
useButton — управление клавиатурным и мышиным
взаимодействием кнопки.useFocusRing — отслеживание фокуса и визуальных
индикаторов для доступности.useHover — реакция на наведение мыши.useOverlayTrigger и useOverlay —
управление модальными или всплывающими окнами.useListBox и useOption — создание списков
с поддержкой клавиатуры и ARIA-ролей.Главная задача — комбинировать хуки так, чтобы их состояния не
дублировались и не перезаписывали друг друга. Для этого каждый
хук возвращает props, которые нужно корректно объединять с
помощью mergeProps из React Aria Utilities:
import { mergeProps } from '@react-aria/utils';
const combinedProps = mergeProps(buttonProps, hoverProps, focusProps);
mergeProps гарантирует, что обработчики событий и
атрибуты ARIA будут объединены без конфликтов.
Для примера создадим кнопку с поддержкой фокуса, наведения и клавиатурного взаимодействия:
import { useButton } from '@react-aria/button';
import { useFocusRing } from '@react-aria/focus';
import { useHover } from '@react-aria/interactions';
function AccessibleButton(props) {
const { buttonProps } = useButton(props);
const { isFocusVisible, focusProps } = useFocusRing();
const { hoverProps, isHovered } = useHover();
const combinedProps = mergeProps(buttonProps, focusProps, hoverProps);
return (
<button {...combinedProps} style={{
outline: isFocusVisible ? '2px solid blue' : 'none',
backgroundColor: isHovered ? '#eee' : '#fff'
}}>
{props.children}
</button>
);
}
Разбор композиции:
useButton обрабатывает нажатия и клавиатурные события
(Enter, Space).useFocusRing добавляет визуальный индикатор фокуса при
навигации с клавиатуры.useHover отслеживает состояние наведения и позволяет
динамически изменять стиль.mergeProps объединяет события и атрибуты всех хуков без
перезаписи.В случае компонентов вроде Select или Menu, требуется объединить несколько хуков:
useListBox — для списка опций с ARIA-ролями и
клавиатурной навигацией.useOption — для каждой опции, чтобы управлять
выделением и состоянием фокуса.useOverlayTrigger и useOverlay — для
управления открытием/закрытием списка.useFocusRing — для визуального фокуса.useHover — для наведения на опции.Пример интеграции хуков для одной опции списка:
import { useOption } from '@react-aria/listbox';
import { useFocusRing } from '@react-aria/focus';
import { useHover } from '@react-aria/interactions';
import { mergeProps } from '@react-aria/utils';
function Option({ item, state }) {
const { optionProps, isSelected, isDisabled } = useOption({ key: item.key }, state);
const { isFocusVisible, focusProps } = useFocusRing();
const { hoverProps, isHovered } = useHover();
const combinedProps = mergeProps(optionProps, focusProps, hoverProps);
return (
<div
{...combinedProps}
style={{
backgroundColor: isSelected ? 'lightblue' : isHovered ? '#eee' : '#fff',
outline: isFocusVisible ? '2px solid blue' : 'none',
opacity: isDisabled ? 0.5 : 1,
padding: '4px 8px',
cursor: isDisabled ? 'not-allowed' : 'pointer'
}}
>
{item.label}
</div>
);
}
Здесь каждый хук отвечает за свой аспект поведения:
useOption — управление выделением и ролями ARIA.useFocusRing — визуальный индикатор фокуса.useHover — реакция на наведение мыши.Ключевой момент — использование
mergeProps предотвращает конфликт обработчиков
onClick, onKeyDown, onMouseEnter
и onMouseLeave.
mergeProps: объединение
props обязательное при комбинировании нескольких хуков.import { useButton } from '@react-aria/button';
import { useFocusRing } from '@react-aria/focus';
import { useHover } from '@react-aria/interactions';
import { mergeProps } from '@react-aria/utils';
import { useState } from 'react';
function useTooltipButton(props) {
const { buttonProps } = useButton(props);
const { isFocusVisible, focusProps } = useFocusRing();
const { hoverProps, isHovered } = useHover();
const [showTooltip, setShowTooltip] = useState(false);
const combinedProps = mergeProps(buttonProps, focusProps, hoverProps, {
onMouseEnter: () => setShowTooltip(true),
onMouseLeave: () => setShowTooltip(false)
});
return { combinedProps, isHovered, isFocusVisible, showTooltip };
}
Такой хук позволяет многократно использовать комбинацию поведения кнопки с подсказкой, не дублируя код.
Композиция хуков в React Aria обеспечивает максимальную гибкость и контроль, позволяя создавать доступные и интерактивные интерфейсы с минимальным количеством конфликтов и чистой архитектурой компонентов.