React Aria предоставляет набор хуков и утилит для создания доступных
интерфейсов, где управление видимостью элементов играет ключевую роль. В
основе работы с видимыми элементами лежит концепция видимости в
DOM и состояния компонентов, а также
правильное взаимодействие с aria-* атрибутами для
обеспечения доступности.
useOverlay и
useModalХук useOverlay отвечает за управление появлением
и исчезновением элементов интерфейса, таких как всплывающие
панели или меню. Основные параметры:
import { useOverlay } from "@react-aria/overlays";
function MyOverlay({ isOpen, onClose }) {
let ref = useRef();
let { overlayProps } = useOverlay({ isOpen, onClose, isDismissable: true }, ref);
return (
isOpen && (
<div {...overlayProps} ref={ref}>
Содержимое оверлея
</div>
)
);
}
isOpen — состояние видимости
элемента.onClose — функция, вызываемая при
закрытии.isDismissable — позволяет закрывать
оверлей кликом вне его области.useFocusRing
для визуализации состоянияuseFocusRing помогает выделять элементы, которые
находятся в фокусе. Это важно для доступности при динамическом
отображении элементов:
import { useFocusRing } from "@react-aria/focus";
function FocusableButton() {
let { isFocusVisible, focusProps } = useFocusRing();
return (
<button {...focusProps} style={{ outline: isFocusVisible ? '2px solid blue' : 'none' }}>
Кнопка
</button>
);
}
Для динамических списков (например, фильтруемых таблиц или меню) React Aria предоставляет хуки:
useListState — управляет состоянием
элементов списка.useListBox — добавляет доступные
атрибуты для списков.Пример:
import { useListState } from "@react-stately/list";
import { useListBox, useOption } from "@react-aria/listbox";
function MyListBox({ items }) {
let state = useListState({ items });
let ref = useRef();
let { listBoxProps } = useListBox({ ref });
return (
<ul {...listBoxProps} ref={ref}>
{[...state.collection].map(item => {
let { optionProps, isSelected } = useOption({ key: item.key }, state);
return (
<li {...optionProps} style={{ background: isSelected ? 'lightgray' : 'white' }}>
{item.rendered}
</li>
);
})}
</ul>
);
}
state.collection — коллекция всех
элементов.isSelected — управляет визуальным
состоянием выделения.optionProps — автоматически добавляет
aria-selected и обработчики событий.useHover и usePressReact Aria позволяет отслеживать состояние интеракций, чтобы управлять видимостью элементов при наведении или клике:
import { useHover, usePress } from "@react-aria/interactions";
function InteractiveBox() {
let ref = useRef();
let { hoverProps, isHovered } = useHover({ ref });
let { pressProps, isPressed } = usePress({ ref });
return (
<div
{...hoverProps}
{...pressProps}
ref={ref}
style={{
background: isHovered ? 'lightblue' : 'white',
transform: isPressed ? 'scale(0.95)' : 'scale(1)',
}}
>
Интерактивный блок
</div>
);
}
isHovered и
isPressed — используются для динамической
визуальной обратной связи.Для больших списков и таблиц важно не рендерить все элементы сразу, а использовать виртуализацию совместно с React Aria:
useVirtualizer или библиотек типа
react-virtual позволяет рассчитывать только видимые
элементы, снижая нагрузку на DOM.useListState или
useTableState делает виртуализацию доступной и совместимой
с ARIA.import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }) {
const parentRef = useRef();
const rowVirtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
});
return (
<div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
<div style={{ height: `${rowVirtualizer.getTotalSize()}px`, position: 'relative' }}>
{rowVirtualizer.getVirtualItems().map(virtualRow => (
<div
key={virtualRow.index}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
transform: `translateY(${virtualRow.start}px)`,
}}
>
{items[virtualRow.index]}
</div>
))}
</div>
</div>
);
}
role и aria-* атрибутов через
useListBox или useTable.Ключевой момент — все динамически отображаемые элементы должны корректно оповещать вспомогательные технологии:
aria-hidden для скрытых
элементов, чтобы они не мешали навигации.role="dialog" или role="menu"
для контейнеров с оверлеями.focusScope из
@react-aria/focus:import { FocusScope } from "@react-aria/focus";
<FocusScope contain restoreFocus autoFocus>
<div role="dialog">
Контент модального окна
</div>
</FocusScope>
contain ограничивает фокус внутри
оверлея.restoreFocus возвращает фокус к
предыдущему элементу после закрытия.autoFocus автоматически устанавливает
фокус на первый доступный элемент.Расчёт видимых элементов в React Aria строится на сочетании состояний, хуков для взаимодействия и ARIA-атрибутов, что позволяет создавать производительные и доступные интерфейсы даже при больших объёмах данных.