Виртуализация — это техника оптимизации рендеринга длинных списков
или сеток элементов, при которой в DOM одновременно отображается только
часть видимых элементов. Это значительно повышает производительность,
снижает нагрузку на браузер и ускоряет взаимодействие с интерфейсом.
Библиотека React Aria предоставляет хук
useVirtualizer, который реализует гибкую и эффективную
виртуализацию списков и сеток.
Для работы с виртуализированными списками сначала нужно импортировать хук:
import { useVirtualizer } from '@react-aria/virtualizer';
Основной принцип работы useVirtualizer заключается в
том, что он возвращает массив видимых элементов и вычисляет смещения для
корректного позиционирования, имитируя полный список в DOM.
Простейший пример виртуализированного списка:
function VirtualList({ items }) {
const rowVirtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => document.getElementById('scroll-container'),
estimateSize: () => 50, // предполагаемая высота элемента
});
return (
<div id="scroll-container" style={{ height: '400px', overflow: 'auto' }}>
<div style={{ height: rowVirtualizer.totalSize, position: 'relative' }}>
{rowVirtualizer.virtualItems.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>
);
}
count — общее количество элементов в списке.
useVirtualizer использует это значение для расчета
диапазона видимых элементов.
getScrollElement — функция, возвращающая контейнер с прокруткой. Важно передавать реальный DOM-элемент.
estimateSize — функция или константа, определяющая приблизительную высоту (или ширину для горизонтальной прокрутки) каждого элемента. Это позволяет виртуализатору корректно вычислять смещение и размер контейнера.
virtualItems — массив объектов, каждый из которых содержит:
index — индекс элемента в исходном массиве.start — смещение элемента относительно начала
контейнера.size — фактический размер элемента после
измерения.useVirtualizer поддерживает как вертикальную, так и
горизонтальную виртуализацию. По умолчанию используется вертикальная
ориентация, но для горизонтальной можно указать
horizontal: true:
const columnVirtualizer = useVirtualizer({
count: items.length,
horizontal: true,
getScrollElement: () => document.getElementById('horizontal-scroll'),
estimateSize: () => 120
});
В этом случае смещение start будет рассчитываться по оси
X, а трансформация будет translateX.
В реальных приложениях элементы списка могут иметь переменную высоту.
useVirtualizer позволяет учитывать это через функцию
измерения:
const rowVirtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => document.getElementById('scroll-container'),
estimateSize: index => items[index].height // возвращает конкретную высоту для каждого элемента
});
После рендеринга виртуализатор автоматически обновляет размеры и перерассчитывает смещения для точного позиционирования элементов.
Для программного скроллинга к определённому элементу можно
использовать метод scrollToIndex:
rowVirtualizer.scrollToIndex(50, { align: 'center' });
Параметры:
start, center, end или
auto).Это позволяет легко реализовать функции вроде «перейти к новому сообщению» или «найти элемент по поиску».
useVirtualizer отлично сочетается с коллекциями React
Aria (useListState, useGridState) для создания
полностью доступных и оптимизированных списков и таблиц. Например:
import { useListState } from '@react-stately/list';
function AccessibleVirtualList({ items }) {
const state = useListState({ items });
const rowVirtualizer = useVirtualizer({
count: state.collection.size,
getScrollElement: () => document.getElementById('scroll-container'),
estimateSize: () => 50
});
return (
<div id="scroll-container" style={{ height: '400px', overflow: 'auto' }}>
<div style={{ height: rowVirtualizer.totalSize, position: 'relative' }}>
{rowVirtualizer.virtualItems.map(virtualRow => {
const item = state.collection.getItem(virtualRow.index);
return (
<div
key={item.key}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
transform: `translateY(${virtualRow.start}px)`
}}
>
{item.rendered}
</div>
);
})}
</div>
</div>
);
}
Такое сочетание обеспечивает не только высокую производительность, но и полное соответствие стандартам доступности, включая правильную работу с клавиатурой и поддержкой ARIA-атрибутов.
React.memo для
предотвращения лишних перерисовок.overscan можно рендерить несколько элементов за
пределами видимой области, уменьшая мерцание при прокрутке:const rowVirtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => document.getElementById('scroll-container'),
estimateSize: () => 50,
overscan: 5
});
ResizeObserver позволяет автоматически обновлять размеры
элементов при изменении контента.Для сеток (grid) useVirtualizer
поддерживает двухмерную виртуализацию с указанием количества столбцов и
размеров элементов:
const gridVirtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => document.getElementById('grid-scroll'),
estimateSize: () => 100,
horizontal: false,
getItemKey: index => items[index].id,
columns: 4
});
Каждый элемент получает позицию row и
column, что позволяет точно отображать сетку с тысячами
элементов без падения производительности.
useVirtualizer предоставляет полный набор инструментов
для построения высокопроизводительных списков и сеток, включая поддержку
переменной высоты элементов, горизонтальной прокрутки, интеграцию с
коллекциями React Aria и управление скроллингом. Правильная настройка
параметров estimateSize, overscan и методов
прокрутки позволяет создать плавный и отзывчивый интерфейс даже для
огромных наборов данных.