Відрендерити 20 000 рядків таблиці - це 20 000 компонентів і сотні тисяч DOM-вузлів. Повільно все: перший рендер, кожне оновлення, прокрутка, пам'ять. memo тут не рятує - проблема в самій кількості.
Віртуалізація (windowing) - рендерити лише рядки, видимі на екрані, плюс невеликий запас. При прокрутці ті самі DOM-вузли отримують нові дані.
import { useVirtualizer } from '@tanstack/react-virtual';
function VacancyList({ items }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 72, // орієнтовна висота рядка
overscan: 5,
});
return (
<div ref={parentRef} style={{ height: 600, overflow: 'auto' }}>
<div style={{ height: virtualizer.getTotalSize(), position: 'relative' }}>
{virtualizer.getVirtualItems().map((row) => (
<div
key={row.key}
style={{ position: 'absolute', top: 0, transform: `translateY(${row.start}px)`, width: '100%' }}
>
<VacancyRow vacancy={items[row.index]} />
</div>
))}
</div>
</div>
);
}
У DOM постійно ~15 рядків замість 20 000, а висота контейнера дорівнює повній висоті списку - смуга прокрутки виглядає правильно.
Бібліотеки: TanStack Virtual (без готової розмітки, гнучкий), react-window, react-virtuoso (вміє динамічну висоту й «прилипання» до низу для чатів).
Складнощі, які треба врахувати:
- рядки різної висоти - вимірювати після рендеру (
measureElement) і оцінювати заздалегідь; - пошук браузера (Ctrl+F) не знайде текст рядків, яких немає в DOM;
- доступність: зчитувачі екрана бачать лише відрендерені рядки - потрібні
aria-rowcount/aria-rowindex; - збереження позиції прокрутки при поверненні на сторінку.
Альтернативи, які часто кращі:
- пагінація чи «завантажити ще» - користувач рідко переглядає 20 000 рядків;
- фільтри й пошук на сервері - показувати лише релевантне;
- CSS
content-visibility: auto- браузер пропускає розкладку й малювання позаекранних блоків, хоча DOM лишається повним. Простий виграш для довгих сторінок з помірною кількістю елементів.
Віртуалізація потрібна, коли весь набір справді має бути доступний прокруткою: логи, таблиці даних, великі чати.