Бесконечная прокрутка на React

Никита Калашников
Введение
Иногда появляются задачи на вывод большого кол-ва элементов, без возможности подгрузки порциями с помощью пагинации или прокрутки страницы пользователем.
В таком случае без оптимизированного решения приложение будет тормозить, что естественно не соотвествует логике использования React. Для таких сценариев подойдут виртуализация и библиотека react-window
.
Зачем использовать react-window?
react-window
позволяет рендерить только видимые элементы списка, что минимизирует потребление памяти и увеличивает производительность. Вместо отображения всех элементов, библиотека создает небольшое количество DOM-узлов, которые обновляются по мере прокрутки.
Установка
Для начала установим необходимые зависимости:
1npm install react-window
Если проект использует TypeScript, убедитесь, что установлены типы для библиотеки:
1npm install --save-dev @types/react-window
Реализация бесконечной ленты
Компонент List с использованием react-window
Создадим компонент для отображения списка:
1import React from 'react';
2import { FixedSizeList as List } from 'react-window';
3
4type ItemProps = {
5 index: number;
6 style: React.CSSProperties;
7};
8
9const items = Array.from({ length: 1000 }, (_, index) => `Элемент ${index + 1}`);
10
11const Row: React.FC<ItemProps> = ({ index, style }) => (
12 <div style={style}>
13 {items[index]}
14 </div>
15);
16
17const VirtualizedList: React.FC = () => {
18 return (
19 <List
20 height={500} // Высота видимой области
21 itemCount={items.length} // Количество элементов
22 itemSize={35} // Высота одного элемента
23 width={300} // Ширина списка
24 >
25 {Row}
26 </List>
27 );
28};
29
30export default VirtualizedList;
Объяснение кода
- Компонент Row: Отвечает за отрисовку одного элемента списка.
- Свойства List:
Бесконечная загрузка данных
Для реализации подгрузки элементов по мере прокрутки потребуется добавить обработчик события скроллинга.
1import React, { useState, useCallback } from 'react';
2import { FixedSizeList as List } from 'react-window';
3
4type ItemProps = {
5 index: number;
6 style: React.CSSProperties;
7};
8
9const generateItems = (start: number, count: number) =>
10 Array.from({ length: count }, (_, index) => `Элемент ${start + index + 1}`);
11
12const InfiniteScrollList: React.FC = () => {
13 const [items, setItems] = useState(() => generateItems(0, 100));
14
15 const loadMoreItems = useCallback(() => {
16 setItems((prevItems) => [...prevItems, ...generateItems(prevItems.length, 50)]);
17 }, []);
18
19 const isItemLoaded = (index: number) => index < items.length;
20
21 const Row: React.FC<ItemProps> = ({ index, style }) => {
22 if (!isItemLoaded(index)) {
23 return <div style={style}>Загрузка...</div>;
24 }
25
26 return <div style={style}>{items[index]}</div>;
27 };
28
29 return (
30 <List
31 height={500}
32 itemCount={items.length + 1} // +1 для индикатора загрузки
33 itemSize={35}
34 width={300}
35 onItemsRendered={({ visibleStopIndex }) => {
36 if (visibleStopIndex === items.length - 1) {
37 loadMoreItems();
38 }
39 }}
40 >
41 {Row}
42 </List>
43 );
44};
45
46export default InfiniteScrollList;
Ключевые моменты реализации
generateItems
: Функция для создания новых элементов.loadMoreItems
: Подгружает дополнительные элементы при достижении конца списка.onItemsRendered
: Отслеживает видимые элементы и инициирует загрузку при необходимости.
Заключение
Использование react-window
в связке с React и TypeScript позволяет легко реализовать производительное приложение с бесконечной лентой. Виртуализация сокращает использование ресурсов, а подгрузка данных обеспечивает масштабируемость.