Библиотека React Aria предоставляет мощный набор хуков и утилит для
создания доступных пользовательских интерфейсов в React. Навигация
стрелками — один из ключевых аспектов, обеспечивающий удобство работы с
компонентами списка, таблицы, меню или табов с клавиатуры. Она
реализуется с помощью хуков useKeyboard,
useFocusRing, useListState и компонентов из
react-aria и react-stately.
Для реализации навигации стрелками необходимо контролировать фокус на элементе списка. В React Aria это делается через:
useFocusRing — определяет, находится
ли элемент в фокусе, и позволяет корректно отображать индикатор
фокуса.useKeyboard — отслеживает нажатия
клавиш и позволяет реагировать на стрелки вверх/вниз, влево/вправо.Пример базового использования:
import {useFocusRing} from '@react-aria/focus';
import {useKeyboard} from '@react-aria/interactions';
import {useRef} from 'react';
function ListItem({item, onActivate}) {
let ref = useRef();
let {isFocusVisible, focusProps} = useFocusRing();
let {keyboardProps} = useKeyboard({
onKeyDown: (e) => {
if (e.key === 'Enter' || e.key === ' ') {
onActivate(item);
}
}
});
return (
<div
{...focusProps}
{...keyboardProps}
ref={ref}
style={{outline: isFocusVisible ? '2px solid blue' : 'none'}}
>
{item.name}
</div>
);
}
Здесь useFocusRing управляет видимостью фокуса, а
useKeyboard реагирует на нажатия клавиш Enter и Space для
активации элемента.
useListState для управления коллекциейuseListState из react-stately позволяет
хранить состояние элементов коллекции и управлять выбором и фокусом.
import {useListState} from '@react-stately/list';
let state = useListState({items: data});
Состояние state содержит:
selectedKeys — выбранные элементыfocusedKey — текущий элемент в фокусеsetSelectedKeys и setFocusedKey для
управления состояниемИспользуя эти методы, можно реализовать навигацию стрелками следующим образом:
function handleKeyDown(e) {
if (e.key === 'ArrowDown') {
let nextKey = state.collection.getKeyAfter(state.focusedKey);
if (nextKey) state.setFocusedKey(nextKey);
}
if (e.key === 'ArrowUp') {
let prevKey = state.collection.getKeyBefore(state.focusedKey);
if (prevKey) state.setFocusedKey(prevKey);
}
}
Функции getKeyAfter и getKeyBefore
предоставляют безопасный способ перемещения фокуса по коллекции,
учитывая вложенные группы и скрытые элементы.
Для меню часто используется комбинация хуков useMenu,
useMenuItem и состояния useListState. Основная
логика:
let state = useListState({items: menuItems});
function onKeyDown(e) {
if (e.key === 'ArrowDown') {
let nextKey = state.collection.getKeyAfter(state.focusedKey);
if (nextKey) state.setFocusedKey(nextKey);
e.preventDefault();
}
if (e.key === 'ArrowUp') {
let prevKey = state.collection.getKeyBefore(state.focusedKey);
if (prevKey) state.setFocusedKey(prevKey);
e.preventDefault();
}
if (e.key === 'Enter') {
state.selectionManager.select(state.focusedKey);
}
}
<MenuItem
key={item.key}
item={item}
isFocused={state.focusedKey === item.key}
/>
MenuItem использует useFocusRing для
отображения индикатора и useKeyboard для обработки действий
по клавишам.
Для табов и горизонтальных списков стрелки влево и вправо применяются аналогично:
function onKeyDown(e) {
if (e.key === 'ArrowRight') {
let nextKey = state.collection.getKeyAfter(state.focusedKey);
if (nextKey) state.setFocusedKey(nextKey);
e.preventDefault();
}
if (e.key === 'ArrowLeft') {
let prevKey = state.collection.getKeyBefore(state.focusedKey);
if (prevKey) state.setFocusedKey(prevKey);
e.preventDefault();
}
}
Важно, что библиотека корректно обрабатывает циклический фокус при
использовании collection.getKeyAfter и
collection.getKeyBefore, что позволяет реализовать
“кольцевую” навигацию без дополнительных проверок.
React Aria автоматически применяет необходимые ARIA-атрибуты для элементов с фокусом:
aria-selected для выбранного элементаaria-activedescendant для контейнеров с управляемым
фокусомrole="listbox" и role="option" для списков
и элементовЭто гарантирует корректное взаимодействие с экранными читалками при навигации стрелками.
Навигация стрелками часто комбинируется с:
useTypeSelect — позволяет выбирать
элементы по вводу текстаuseSelectableCollection — для
управления множественным выборомuseOverlay — для меню, тултипов и
всплывающих списковПример интеграции с useTypeSelect:
import {useTypeSelect} from '@react-aria/selection';
let {typeSelectProps} = useTypeSelect({keyboardDelegate: state.keyboardDelegate, selectionManager: state.selectionManager});
Это добавляет возможность быстро прыгать к элементам списка по первым буквам при сохранении полноценной навигации стрелками.
key для элементов списка для корректного
восстановления состояния при ререндере.React.memo.Навигация стрелками в React Aria позволяет создавать доступные, удобные и предсказуемые интерфейсы, которые корректно взаимодействуют с клавиатурой и вспомогательными технологиями, минимизируя необходимость ручного управления фокусом и состоянием элементов.