← Назад к вопросам

Как сделать эффективную пагинацию на React без значительного влияния на производительность?

2.2 Middle🔥 242 комментариев
#React

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Эффективная пагинация в React: подходы и оптимизации

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

1. Lazy Loading и Infinite Scroll

Вместо загрузки всех данных сразу используйте постепенную подгрузку при прокрутке. Это кардинально снижает первоначальную нагрузку.

import { useState, useEffect, useCallback } from 'react';

const InfiniteScrollPaginator = () => {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);

  const loadMore = useCallback(async () => {
    if (loading) return;
    
    setLoading(true);
    const newItems = await fetchPageData(page);
    setItems(prev => [...prev, ...newItems]);
    setPage(prev => prev + 1);
    setLoading(false);
  }, [page, loading]);

  useEffect(() => {
    const handleScroll = () => {
      if (window.innerHeight + document.documentElement.scrollTop >= 
          document.documentElement.offsetHeight - 100) {
        loadMore();
      }
    };

    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, [loadMore]);

  return <div>{/* Рендер items */}</div>;
};

2. Virtualization (виртуализация списков)

Для очень больших списков используйте библиотеки виртуализации, которые рендерят только видимые элементы:

import { FixedSizeList as List } from 'react-window';

const VirtualizedList = ({ items }) => (
  <List
    height={500}
    itemCount={items.length}
    itemSize={50}
    width="100%"
  >
    {({ index, style }) => (
      <div style={style}>Элемент {items[index].id}</div>
    )}
  </List>
);

Популярные библиотеки: react-window, react-virtualized, @tanstack/react-virtual.

3. Оптимизация рендеринга компонентов

  • Используйте React.memo для предотвращения лишних ререндеров элементов списка
  • Применяйте useMemo для кэширования вычисляемых данных пагинации
  • Разделяйте состояния: отдельное состояние для данных, загрузки и ошибок
const PaginatedItem = React.memo(({ item }) => {
  return <div>{item.name}</div>;
});

const PaginationContainer = () => {
  const paginatedData = useMemo(() => 
    calculateCurrentPage(data, currentPage, pageSize), 
    [data, currentPage, pageSize]
  );
  
  return paginatedData.map(item => (
    <PaginatedItem key={item.id} item={item} />
  ));
};

4. Продвинутое кэширование данных

Реализуйте многоуровневое кэширование с использованием React Query, SWR или собственных решений:

import { useQuery } from '@tanstack/react-query';

const usePaginatedData = (page) => {
  return useQuery({
    queryKey: ['items', page],
    queryFn: () => fetchItems(page),
    staleTime: 5 * 60 * 1000, // 5 минут кэширования
    keepPreviousData: true // Сохраняем предыдущие данные
  });
};

5. Оптимизация работы с API

  • Предзагрузка данных: Загружайте следующую страницу заранее при приближении к концу списка
  • Дебаунсинг запросов: Предотвращайте множественные запросы при быстром скролле
  • Отмена запросов: Используйте AbortController для отмены ненужных запросов
useEffect(() => {
  const controller = new AbortController();
  
  const fetchData = async () => {
    try {
      const response = await fetch(`/api/items?page=${page}`, {
        signal: controller.signal
      });
      // обработка данных
    } catch (err) {
      if (err.name !== 'AbortError') {
        // обработка ошибки
      }
    }
  };
  
  fetchData();
  
  return () => controller.abort();
}, [page]);

6. Оптимизация навигации

  • Используйте URL параметры для синхронизации состояния пагинации
  • Реализуйте предсказуемую навигацию с помощью кнопок "Назад/Вперед"
  • Добавьте пропуск страниц для больших диапазонов

7. Комбинированный подход на практике

const OptimizedPagination = () => {
  const [currentPage, setCurrentPage] = useState(1);
  const { data, isLoading, isFetching } = usePaginatedData(currentPage);
  const [visibleRange, setVisibleRange] = useState({ start: 0, end: 20 });
  
  // Виртуализация рендеринга
  const visibleItems = useMemo(() => 
    data?.slice(visibleRange.start, visibleRange.end) || [],
    [data, visibleRange]
  );
  
  // Предзагрузка следующей страницы
  useEffect(() => {
    if (visibleRange.end > data.length - 10 && !isFetching) {
      const nextPage = currentPage + 1;
      prefetchPage(nextPage); // Функция предзагрузки
    }
  }, [visibleRange, data, currentPage, isFetching]);
  
  return (
    <>
      <VirtualizedList items={visibleItems} />
      {isLoading && <LoadingIndicator />}
      <PaginationControls 
        currentPage={currentPage}
        totalPages={totalPages}
        onChangePage={setCurrentPage}
      />
    </>
  );
};

Ключевые рекомендации:

  • Измеряйте производительность с помощью React DevTools Profiler
  • Разбивайте компоненты на мелкие, переиспользуемые части
  • Тестируйте с реальными объемами данных (10k+ элементов)
  • Используйте ленивую загрузку изображений внутри элементов списка
  • Оптимизируйте сортировку и фильтрацию на стороне сервера

Эффективная пагинация — это баланс между пользовательским опытом (минимальные задержки, плавный скролл) и ресурсоемкостью (оптимальное использование памяти и сетевых запросов). Начинайте с простой реализации, затем постепенно добавляйте оптимизации на основе метрик производительности и реальных потребностей вашего приложения.

Как сделать эффективную пагинацию на React без значительного влияния на производительность? | PrepBro