Imagine you’re reading a long article online. Instead of having to click “Next Page” every time you reach the bottom, the page just keeps loading more content as you scroll down. This is called infinite scrolling.
Let’s see how to do it in React:
import React, { useState, useEffect } from 'react';
const InfiniteScrollList = () => {
  const [items, setItems] = useState([]); // List of items
  const [page, setPage] = useState(1); // Tracks the current page
  const [loading, setLoading] = useState(false); // Loading state
  const [error, setError] = useState(null); // Error state for handling fetch errors
  // Fetch more items whenever the page changes
  useEffect(() => {
    fetchMoreItems();
  }, [page]);
  const fetchMoreItems = async () => {
    setLoading(true);
    setError(null); // Reset error state before new fetch
    try {
      const newItems = await fakeApiCall(page);
      setItems(prevItems => [...prevItems, ...newItems]);
    } catch (err) {
      setError('Error fetching items');
    } finally {
      setLoading(false); // Ensure loading is reset after fetch attempt
    }
  };
  // Handle scroll to detect if the user has reached the bottom
  const handleScroll = () => {
    if (
      window.innerHeight + document.documentElement.scrollTop >=
        document.documentElement.offsetHeight - 50 &&
      !loading
    ) {
      setPage(prevPage => prevPage + 1); // Load the next page
    }
  };
  // Attach the scroll event listener when the component mounts
  useEffect(() => {
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll); // Cleanup on unmount
  }, []);
  return (
    <div>
      <ul>
        {items.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
      {loading && <p>Loading more, please wait...</p>}
      {error && <p style={{ color: 'red' }}>{error}</p>} {/* Display error if any */}
    </div>
  );
};
// Simulated API call (returns 10 new items per page)
const fakeApiCall = page => {
  return new Promise(resolve => {
    setTimeout(() => {
      const newItems = Array.from({ length: 10 }, (_, index) => ({
        id: page * 10 + index,
        name: `Item ${page * 10 + index}`,
      }));
      resolve(newItems);
    }, 1000);
  });
};
export default InfiniteScrollList;
Related Question : Implement role-based access control in a full-stack application