Логотип

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

Енов в телефоне листает сообщения
126

Введение

Иногда появляются задачи на вывод большого кол-ва элементов, без возможности подгрузки порциями с помощью пагинации или прокрутки страницы пользователем.

В таком случае без оптимизированного решения приложение будет тормозить, что естественно не соотвествует логике использования 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;

Объяснение кода

  1. Компонент Row: Отвечает за отрисовку одного элемента списка.
  2. Свойства 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;

Ключевые моменты реализации

  1. generateItems: Функция для создания новых элементов.
  2. loadMoreItems: Подгружает дополнительные элементы при достижении конца списка.
  3. onItemsRendered: Отслеживает видимые элементы и инициирует загрузку при необходимости.

Заключение

Использование react-window в связке с React и TypeScript позволяет легко реализовать производительное приложение с бесконечной лентой. Виртуализация сокращает использование ресурсов, а подгрузка данных обеспечивает масштабируемость.