useMenuItem — это хук из библиотеки React
Aria, предназначенный для управления поведением отдельных
пунктов меню с учетом доступности и взаимодействия с пользователем. Он
обеспечивает правильную обработку фокуса, навигации с клавиатуры,
состояния выбранности и активации элементов меню. Этот хук особенно
полезен при создании сложных, многоуровневых меню с динамическим
содержимым.
Для работы с useMenuItem необходимы
React, React Aria, а также
React Stately для управления состоянием меню:
import { useMenuItem } from '@react-aria/menu';
import { useMenuState } from '@react-stately/menu';
import { useFocusRing } from '@react-aria/focus';
import { useRef } from 'react';
Простейший пример использования:
function MenuItem({ item, state }) {
const ref = useRef();
const { menuItemProps, isFocused, isSelected } = useMenuItem(
{ key: item.key },
state,
ref
);
return (
<div
{...menuItemProps}
ref={ref}
style={{
background: isFocused ? 'lightblue' : 'transparent',
fontWeight: isSelected ? 'bold' : 'normal',
padding: '4px 8px',
cursor: 'pointer'
}}
>
{item.name}
</div>
);
}
Разбор ключевых моментов:
menuItemProps — содержит все необходимые свойства для
корректного взаимодействия элемента меню с клавиатурой, мышью и
ассистивными технологиями. Их нужно распространять на корневой элемент
пункта меню.isFocused — указывает, что пункт меню находится в
фокусе, что важно для визуальной подсветки.isSelected — отображает, выбран ли пункт меню (для
радио или чекбоксов внутри меню).useMenuItem принимает три аргумента:
props — объект с настройками пункта меню:
key — уникальный идентификатор пункта меню
(обязательный).isDisabled — отключает пункт меню.isSelected — задает состояние выбранности пункта.onAction — callback, вызываемый при активации
пункта.state — состояние меню, созданное с помощью
useMenuState. Отвечает за управление активным пунктом,
выбранными элементами и закрытием меню.
ref — ссылка на DOM-элемент пункта меню. Используется для управления фокусом и взаимодействия с клавиатурой.
Пример передачи этих параметров:
const { menuItemProps, isFocused, isSelected } = useMenuItem(
{
key: 'save',
isDisabled: false,
onAction: () => console.log('Сохранение выполнено')
},
menuState,
itemRef
);
useMenuItem автоматически обрабатывает:
Для корректной работы необходимо, чтобы контейнер
меню использовал useMenu из React Aria, а
состояние управлялось через useMenuState.
import { useMenu } from '@react-aria/menu';
function Menu({ state }) {
const ref = useRef();
const { menuProps } = useMenu({}, state, ref);
return (
<ul {...menuProps} ref={ref}>
{state.collection.map(item => (
<MenuItem key={item.key} item={item} state={state} />
))}
</ul>
);
}
useMenuItem обеспечивает правильные атрибуты для
ассистивных технологий:
role="menuitem" — основной атрибут для элементов
меню.aria-disabled — если элемент отключен.aria-selected — если пункт выбран.tabIndex — управление фокусом при навигации.Совместно с useFocusRing можно добавить визуальный
индикатор фокуса, который также учитывается для пользователей с
ограниченными возможностями.
const { isFocusVisible, focusProps } = useFocusRing();
<div
{...menuItemProps}
{...focusProps}
ref={ref}
style={{
outline: isFocusVisible ? '2px solid blue' : 'none'
}}
>
{item.name}
</div>
Для вложенных меню useMenuItem поддерживает
hasSubmenu и автоматически добавляет
aria-haspopup и aria-expanded. Это позволяет
создавать многоуровневые навигационные структуры без ручного управления
семантикой.
<MenuItem
key="file"
item={{ key: 'file', name: 'Файл' }}
state={menuState}
hasSubmenu={true}
/>
key для каждого пункта
меню.useRef для элементов меню, чтобы React
Aria мог управлять фокусом.useMenuState.useMenuItem превращает обычный <div>
или <li> в полностью доступный, интерактивный пункт
меню, автоматически управляя фокусом, состояниями и навигацией, что
делает интерфейсы с меню безопасными и удобными для всех
пользователей.