Работа с большими коллекциями элементов в веб-приложениях часто сталкивается с проблемами производительности. При прямом рендеринге всех элементов списка DOM быстро разрастается, что приводит к торможению интерфейса и задержкам в отклике пользователя. React Aria предоставляет инструменты для управления такими сценариями, обеспечивая доступность и оптимизацию рендеринга.
Для больших списков ключевым подходом является
виртуализация — рендеринг только видимых элементов на
экране. React Aria не предоставляет встроенный компонент виртуализации,
но тесно интегрируется с библиотеками вроде react-virtual
или react-window. Основная идея заключается в
следующем:
Пример интеграции с react-virtual:
import { useListState } from '@react-stately/list';
import { useListBox, useOption } from '@react-aria/listbox';
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }) {
const state = useListState({ items });
const parentRef = React.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(), position: 'relative' }}>
{rowVirtualizer.getVirtualItems().map(virtualRow => {
const item = items[virtualRow.index];
return (
<ListItem
key={item.key}
item={item}
style={{
position: 'absolute',
top: 0,
left: 0,
transform: `translateY(${virtualRow.start}px)`
}}
/>
);
})}
</div>
</div>
);
}
function ListItem({ item, style }) {
const ref = React.useRef();
const { optionProps, isSelected } = useOption({ key: item.key }, item, null);
return (
<div {...optionProps} ref={ref} style={style}>
{item.name}
{isSelected && ' (выбрано)'}
</div>
);
}
Ключевые моменты:
useListState и useOption
сохраняет доступность списка для клавиатуры и скринридеров.useVirtualizer вычисляет положение и видимость
элементов без рендеринга всего списка.Даже с виртуализацией неправильное использование состояния может приводить к частым повторным рендерам. Основные техники оптимизации:
React.memo
позволяет предотвратить повторный рендер элементов списка, если их
пропсы не изменились.useListState можно извлекать только нужную часть состояния
для конкретного элемента, чтобы изменение другой части списка не
вызывало перерисовку.Для экстремально больших списков стоит комбинировать виртуализацию с ленивой загрузкой (lazy loading):
React Aria позволяет сохранить полную доступность
даже при динамической загрузке элементов, так как состояние списка
управляется через useListState.
В реальных приложениях элементы списка могут иметь разную высоту. В этом случае фиксированная виртуализация становится неэффективной. Подход:
estimateSize с усреднённой высотой и
корректировать позицию через getItemSize.estimateSize: (index) => measuredSizes[index] ?? 50
Таким образом, даже сложные макеты с переменной высотой остаются отзывчивыми.
React Aria обеспечивает полностью доступные списки, что критично при виртуализации:
useListBox и useOption
автоматически обрабатывает фокусировку.useListState.aria-activedescendant позволяет навигации
работать через виртуализированный DOM.React.memo).Эти методы позволяют создавать быстрые, отзывчивые и доступные интерфейсы с большими списками, используя мощь React Aria в сочетании с современными инструментами виртуализации.