Полное руководство по бесконечной прокрутке в 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` (сентинель) попадает в поле зрения.