В Radix UI виртуализация списков реализуется через компоненты и утилиты, позволяющие эффективно отображать большие массивы данных без значительной нагрузки на DOM. Виртуализация обеспечивает рендеринг только видимой части элементов списка, что критически важно при работе с тысячами или десятками тысяч элементов.
Ключевой подход заключается в создании контейнера с фиксированной высотой и прокруткой, внутри которого отображаются только те элементы, которые попадают в область видимости.
ScrollArea и контейнер виртуализацииRadix UI предоставляет компонент
ScrollArea, который заменяет стандартный
div с overflow: auto и обеспечивает
дополнительные возможности управления скроллом. В комбинации с
виртуализацией он становится базовым контейнером для больших
списков.
Пример базовой структуры:
import * as ScrollArea from '@radix-ui/react-scroll-area';
<ScrollArea.Root style={{ height: 400, width: 300 }}>
<ScrollArea.Viewport>
{ /* Здесь будут виртуализированные элементы */ }
</ScrollArea.Viewport>
<ScrollArea.Scrollbar orientation="vertical">
<ScrollArea.Thumb />
</ScrollArea.Scrollbar>
</ScrollArea.Root>
Важные моменты:
Viewport определяет область видимости контента.Scrollbar и Thumb обеспечивают
стилизованный скролл, полностью совместимый с виртуализацией.Root должна быть фиксированной для корректного
расчёта видимых элементов.Для виртуализации удобно использовать сторонние библиотеки, например,
react-virtual, но Radix UI предоставляет
гибкую архитектуру, позволяющую интегрировать её напрямую.
Пример использования виртуализации с помощью
react-virtual:
import { useVirtual } from '@tanstack/react-virtual';
import * as ScrollArea from '@radix-ui/react-scroll-area';
function VirtualList({ items }) {
const parentRef = React.useRef();
const rowVirtualizer = useVirtual({
size: items.length,
parentRef,
estimateSize: () => 35,
});
return (
<ScrollArea.Root style={{ height: 400, width: 300 }}>
<ScrollArea.Viewport ref={parentRef}>
<div style={{ height: rowVirtualizer.totalSize, position: 'relative' }}>
{rowVirtualizer.virtualItems.map(virtualRow => (
<div
key={virtualRow.index}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: `${virtualRow.size}px`,
transform: `translateY(${virtualRow.start}px)`
}}
>
{items[virtualRow.index]}
</div>
))}
</div>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar orientation="vertical">
<ScrollArea.Thumb />
</ScrollArea.Scrollbar>
</ScrollArea.Root>
);
}
Ключевые аспекты:
parentRef используется для отслеживания видимой
области.rowVirtualizer.totalSize задаёт полную высоту списка,
что необходимо для корректного скролла.transform: translateY, чтобы избежать рендеринга невидимых
элементов.При работе с большими списками важно учитывать следующие моменты:
Виртуализированный список можно сочетать с другими компонентами:
Checkbox и RadioGroup —
для списков с выбором элементов.Tooltip и Popover —
элементы внутри виртуализированного списка могут иметь всплывающие
подсказки без потери производительности.Collapsible — позволяет внедрять
сворачиваемые группы элементов внутри виртуализированного списка.При этом важно учитывать, что события и состояние компонентов должны быть привязаны к индексам виртуальных элементов, а не к их уникальным ключам DOM, чтобы избежать проблем при повторном рендере.
Если элементы списка имеют переменную высоту, Radix UI не предоставляет встроенного механизма. В таких случаях используют сторонние решения, например:
react-virtual с
measureRef — измеряет реальную высоту каждого
элемента.const rowVirtualizer = useVirtual({
size: items.length,
parentRef,
estimateSize: (index) => estimatedHeights[index] || 35,
measureRef: (el, index) => {
if(el) measuredHeights[index] = el.getBoundingClientRect().height;
}
});
Radix UI изначально ориентирован на доступность, поэтому виртуализированные списки могут поддерживать навигацию с клавиатуры:
ArrowUp / ArrowDown для перемещения
фокуса.Home / End для перехода к началу или концу
списка.Tab для перехода между интерактивными элементами внутри
списка.Важно синхронизировать виртуальные позиции с фокусом, чтобы элемент, получивший фокус, всегда был в видимой области:
rowVirtualizer.scrollToIndex(focusedIndex, { align: 'auto' });
При необходимости плавного появления элементов можно использовать
CSS transitions или библиотеки вроде
framer-motion. Основная сложность — синхронизация анимации
с абсолютным позиционированием виртуализированных элементов.
Эффективная виртуализация в Radix UI позволяет создавать масштабируемые и доступные интерфейсы, обеспечивая высокую производительность и совместимость с комплексными компонентами экосистемы.