Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Оптимізація довгих списків». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)Довгі списки оптимізують передусім **віртуалізацією (windowing)** - рендерять лише видимі елементи плюс невеликий "overscan", за допомогою бібліотек на кшталт `react-window`, `react-virtualized` або `@tanstack/react-virtual`. **Ключове:** до цього додають пагінацію, мемоізовані елементи зі стабільними пропсами, стабільний `key` замість індексу та розділення важких обчислень від представлення.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## 1) Віртуалізація (windowing) - must-have Рендеримо лише видимі елементи + невеликий «overscan». **Бібліотеки:** - `react-window` (легка, сучасна) - `react-virtualized` (багатша, важча) - `@tanstack/react-virtual` (головоломка, але гнучка) **Приклад з** `react-window` **(фіксована висота):** ```javascript import { FixedSizeList as List } from 'react-window'; import React from 'react'; const Row = React.memo(({ index, style, data }: any) => { const item = data.items[index]; return ( <div style={style} onClick={() => data.onSelect(item.id)}> {item.title} </div> ); }, (prev, next) => prev.data.items[prev.index] === next.data.items[next.index]); export default function Virtualized({ items, onSelect }: { items: any[], onSelect: (id:string)=>void }) { const itemData = React.useMemo(() => ({ items, onSelect }), [items, onSelect]); return ( <List height={600} itemCount={items.length} itemSize={48} width="100%" overscanCount={6} itemData={itemData} > {Row} </List> ); } ``` **Змінна висота?** Використовуйте `VariableSizeList` + вимірювання висоти (наприклад, через `ResizeObserver`) і кешуйте `getItemSize`. --- ## 2) Пагінація і «нескінченний скрол» - Завантаження робіть **порціями** (limit/offset або cursor). - Рендеріть «плоским» масивом; нові пачки - **append**. - Для детекту кінця - `IntersectionObserver` (без постійних обробників scroll). --- ## 3) Мінімізуємо перерендери елементів - Кожен айтем - **окремий мемо-компонент**: `React.memo(Row, areEqual)`. - Пропси елементів - **стабільні за посиланням**: - обробники через `useCallback`; - складні об'єкти/масиви через `useMemo`; - передавайте `itemId`, а не замикайте `item` в inline-колбеку. - Не використовуйте **індекс як** `key` при вставках/видаленнях/перестановках - беріть стабільний `id`. --- ## 4) Розділяємо обчислення і представлення - Фільтрацію, сортування, агрегації - робіть **один раз** і кешуйте `useMemo`. - При введенні в поле фільтра використовуйте `useDeferredValue` або обгортайте оновлення в `startTransition`, щоб список не «лагав»: ```javascript const [query, setQuery] = React.useState(''); const deferredQuery = React.useDeferredValue(query); // згладжує фільтрацію const filtered = React.useMemo( () => items.filter(x => x.title.includes(deferredQuery)), [items, deferredQuery] ); ``` --- ## 5) Зображення і важкі клітинки - Вмикайте **ліниве завантаження**: `<img loading="lazy" ... />`. - Плейсхолдери/скелетони замість важкого контенту до появи даних. - Уникайте дорогого DOM і складного CSS усередині клітинки (тіні, фільтри). --- ## 6) Події скролу - Не вішайте «ручний» `onScroll` на вікно. Якщо потрібно - **passive listeners** і `requestAnimationFrame` + `throttle`. - При віртуалізації бібліотека вже оптимізує скрол - менше самодіяльності. --- ## 7) Контейнер списку - Задайте явну **висоту**/`max-height` і `overflow: auto;` - браузеру простіше. - Для вкладених списків використовуйте CSS `contain: content;` (обмежує область рендерингу). --- ## 8) Серверний рендер і гідратація - Для SEO/SSR великих списків - віддавайте **обмежену кількість елементів**, решту довантажуйте на клієнті. - У Next.js можна стрімити і довантажувати чанки, а клієнт - віртуалізувати. --- ## 9) Кеш розмірів і реюз DOM - При змінній висоті - тримайте **кеш вимірювань** (id → height). - Уникайте повного скидання кешу при часткових змінах даних. --- ## 10) Діагностика - React DevTools Profiler: шукайте «гарячі» компоненти. - Увімкніть мітки в логах рендера клітинок (див. `console.log('Row', index)`) локально. - Перевірте, чи не створюєте нові об'єкти/функції в JSX.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.