12k
All articles

Полное руководство по бесконечной прокрутке в React

Реализация бесконечной прокрутки в React через пакет или хук на основе IntersectionObserver с учётом производительности, состояний загрузки и граничных случаев.

OpenReplay Team
OpenReplay Team
Полное руководство по бесконечной прокрутке в React

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

Ключевые моменты

  • Изучите 2 практических способа реализации бесконечной прокрутки в React
  • Используйте собственный хук с IntersectionObserver или стороннюю библиотеку
  • Управляйте состояниями загрузки, пагинацией и пустыми результатами

Что такое бесконечная прокрутка?

Бесконечная прокрутка загружает новые данные, когда пользователь прокручивает список до конца. Это устраняет необходимость в пагинации и создает непрерывный опыт просмотра. Вы увидите это в таких продуктах, как Instagram, Twitter и Reddit.

Метод 1: Использование пакета (react-infinite-scroll-component)

Самый простой способ реализовать бесконечную прокрутку в React — использовать хорошо протестированный пакет.

Шаг 1: Установка

npm install react-infinite-scroll-component

Шаг 2: Пример базового использования

import InfiniteScroll from 'react-infinite-scroll-component';

function Feed() {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);

  const fetchMoreData = async () => {
    const res = await fetch(`/api/posts?page=${page}`);
    const newItems = await res.json();
    if (newItems.length === 0) setHasMore(false);
    else {
      setItems(prev => [...prev, ...newItems]);
      setPage(prev => prev + 1);
    }
  };

  return (
    <InfiniteScroll
      dataLength={items.length}
      next={fetchMoreData}
      hasMore={hasMore}
      loader={<h4>Loading...</h4>}
      endMessage={<p>No more results</p>}
    >
      {items.map(item => <div key={item.id}>{item.title}</div>)}
    </InfiniteScroll>
  );
}

Преимущества

  • Быстрая настройка
  • Встроенные состояния загрузки и завершения

Недостатки

  • Меньше контроля над логикой прокрутки
  • Добавляет зависимость от пакета

Метод 2: Собственный хук с IntersectionObserver

Этот метод дает вам полный контроль без дополнительных зависимостей.

Шаг 1: Создание хука

function useInfiniteScroll(callback, ref) {
  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) callback();
    });
    if (ref.current) observer.observe(ref.current);
    return () => observer.disconnect();
  }, [callback, ref]);
}

Шаг 2: Использование в компоненте

function Feed() {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const sentinelRef = useRef(null);

  const loadMore = async () => {
    if (!hasMore) return;
    const res = await fetch(`/api/posts?page=${page}`);
    const newItems = await res.json();
    if (newItems.length === 0) setHasMore(false);
    else {
      setItems(prev => [...prev, ...newItems]);
      setPage(prev => prev + 1);
    }
  };

  useInfiniteScroll(loadMore, sentinelRef);

  return (
    <div>
      {items.map(item => <div key={item.id}>{item.title}</div>)}
      <div ref={sentinelRef} style={{ height: 1 }} />
    </div>
  );
}

Преимущества

  • Полностью настраиваемый
  • Работает с любой логикой прокрутки или API

Недостатки

  • Требует больше настройки
  • IntersectionObserver не поддерживается в IE11

Паттерны и советы для реальных проектов

  • Используйте debounce для вызовов fetch, чтобы избежать перегрузки API
  • Показывайте спиннер или скелетон-загрузчик для лучшего UX
  • Добавьте логику повторных попыток для сетевых ошибок
  • Обрабатывайте крайние случаи, такие как нулевые результаты или пустые страницы
if (items.length === 0 && !hasMore) {
  return <p>No posts found.</p>;
}

Соображения по производительности

  • Используйте библиотеки типа react-window для виртуализации при рендеринге тысяч элементов
  • Мемоизируйте компоненты элементов, чтобы предотвратить повторные рендеры
  • Правильно очищайте наблюдатели, чтобы избежать утечек памяти
useEffect(() => {
  const observer = new IntersectionObserver(...);
  return () => observer.disconnect();
}, []);

Заключение

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

Часто задаваемые вопросы

Какой самый простой способ реализовать бесконечную прокрутку в React?

Использование библиотеки, такой как `react-infinite-scroll-component`, обеспечивает быструю и удобную настройку.

Как остановить загрузку, когда больше нет данных?

Установите флаг `hasMore` в значение false, когда API возвращает пустой массив.

Вредит ли бесконечная прокрутка производительности?

Может, если вы рендерите слишком много элементов. Используйте инструменты виртуализации, такие как `react-window`, для управления большими списками.

Как добавить бесконечную прокрутку только при приближении к низу?

Используйте `IntersectionObserver` для запуска загрузки данных, когда нижний `div` (сентинель) попадает в поле зрения.

Listen to your bugs 🧘, with OpenReplay

See how users use your app and resolve issues fast.
Loved by thousands of developers

We use cookies to improve your experience. By using our site, you accept cookies.