Back

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

Полное руководство по бесконечной прокрутке в 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-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