DEV Community

Elham Najeebullah
Elham Najeebullah

Posted on

React: Create Infinite Scrolling With Intersection Observer API

A simple explanation of how the infinite scrolling component works:

  1. The component makes an initial API request to fetch a set of posts when it first mounts. This is done using a useEffect hook with an empty dependencies array, which means the effect will only run once when the component mounts. The fetch function is used to make the API request, and the response is converted to JSON using the json method. The set of posts is stored in the component's state using the setPosts function, which is called with the data from the API response.

  2. The component uses another useEffect hook to set up an intersection observer that listens for when the bottom of the page is reached. The intersection observer is created using the IntersectionObserver constructor, and it takes a callback function as an argument. This callback function is called with an array of intersection observer entries, which contain information about the element being observed (in this case, the bottom of the page). The intersection observer is set up to trigger the callback function when the element is intersecting with the viewport, which means it is visible on the screen.

  3. When the bottom of the page is reached and the intersection observer's callback function is called, a new API request is made to fetch more posts. This is done using an async function called fetchMorePosts, which is called inside the intersection observer's callback function. The fetchMorePosts function uses the fetch function to make the API request, and the response is converted to JSON using the json method. The new posts are added to the existing set of posts using the setPosts function, which is called with a concatenated array of the previous posts and the new posts. The setLoading function is called with true to display the loading spinner, and the setShowLoading function is called with true to delay the display of the spinner using the setTimeout function.

  4. When the new posts have been fetched and added to the existing set of posts, the component re-renders to display the additional posts. The setLoading function is called with false to hide the loading spinner, and the setShowLoading function is called with false to remove the delay on the display of the spinner.

  5. This process is repeated indefinitely as the user scrolls to the bottom of the page. The intersection observer continues to listen for when the bottom of the page is reached, and the fetchMorePosts function is called to fetch more posts each time.

Here is the code example:

import React, { useEffect, useState, useRef } from "react";
import "./App.css";

function App() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(false);
  const [showLoading, setShowLoading] = useState(false);
  const bottom = useRef(null);
  const delay = 10000; // time in ms

  useEffect(() => {
    async function fetchPosts() {
      const res = await fetch("https://jsonplaceholder.typicode.com/posts");
      const data = await res.json();
      setPosts(data);
    }
    fetchPosts();
  }, []);

  useEffect(() => {
    const observer = new IntersectionObserver((entries) => {
      if (entries[0].isIntersecting) {
        async function fetchMorePosts() {
          setLoading(true);
          setTimeout(() => setShowLoading(true), delay);
          const res = await fetch("https://jsonplaceholder.typicode.com/posts");
          const data = await res.json();
          setPosts((prevPosts) => [...prevPosts, ...data]);
          setLoading(false);
          setShowLoading(false);
        }
        fetchMorePosts();
      }
    });
    observer.observe(bottom.current);
  }, []);

  return (
    <div className="infinite-scroll-container">
      <ul>
        {posts.map((post) => (
          <li key={post.id} className="post-card">
            <div className="post-card-header">
              <img className="avatar" src="path/to/avatar.jpg" alt="Avatar" />
              <div className="post-card-header-text">
                <h3 className="username">{post.username}</h3>
                <p className="post-date">{post.date}</p>
              </div>
            </div>
            <div className="post-card-body">
              <p className="post-text">{post.title}</p>
            </div>
            <div className="post-card-footer">
              <button className="like-button">Like</button>
              <button className="comment-button">Comment</button>
            </div>
          </li>
        ))}
      </ul>
      {loading && (
        <div
          className={showLoading ? "loading" : ""}
          style={{ opacity: showLoading ? 1 : 0 }}
        >
          Loading...
        </div>
      )}
      <div ref={bottom} />
    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

I hope this helps! Let me know if you have any questions or need further assistance.

Oldest comments (0)