DEV Community

Cover image for Infinite Scrolling Using Intersection Observer API
Ashish Kumar Saini
Ashish Kumar Saini

Posted on

Infinite Scrolling Using Intersection Observer API

We interact with many internet-based platforms in our day to day life. We use several social media platform and shopping sites. It is easy to shop through an online shopping platform. We scroll up and down to see the products or news feed. As we go down the new set of products or news feed loads automatically.
Infinite scrolling is shown in the following gif.
Alt Text
Without wasting time, now, I come to the point. I discussed the loading of the new set of products during scrolling the UI above. This loading of the new set of products is called pagination of the data.

Pagination:- Pagination is a sequence of pages which are connected and have similar content.

Pagination can be done in several ways. Pagination can be done with a navigation bar at the bottom or top of the page using an anchor tag with href attribute. I m not going deep into it.
This article is on the subject that how should do infinite scrolling using "Intersection Observer API" in react. How can we do pagination in the react using "Intersection Observer API"?
Before the start, I assume that you are familiar with javascript, react, react hooks and intersection observer API. If you are not familiar with intersection observer API then you should first take a look at Intersection Observer API.

Let's start now!!!.

In this article, I am using Github API for the demo of the subject. I'll fetch repos of the user and will show them on the screen.

Alt Text

First, I created a react app and made an InfiniteScrolling react functional component.
Now, I made repoData, totalRepos, currentPage states in the function.

function InfiniteScrolling() {
  const [repoData, setRepoData] = useState([]);
  const [totalRepos, setTotalRepos] = useState(0);
  const [currentPage, setCurrentPage] = useState(1);

  function fetchData() {
    (async () => {
      const data = await fetch(`https://api.github.com/users/praveen-me/repos?
page=${totalRepos ? currentPage : 1}`).then(response => response.json());

      if (currentPage < totalRepos / 30) {
        setCurrentPage(currentPage + 1);
      }

      setRepoData([...repoData, ...data]);
    })()
  }

  useEffect(() => {
    (async () => {
      const userData = await fetch("https://api.github.com/users/praveen-me")
.then(response => response.json());
      setTotalRepos(userData.public_repos);
    })()

    fetchData();
  }, [])

  return (
    <div className="App">
      {repoData.map((repo, index) => {
      return <h1 className="repo" key={repo.name + index}>{repo.name}</h1>
    })}
    </div>
  );

}
Enter fullscreen mode Exit fullscreen mode

FetchData() function is fetching data from the given URI and setting to the states. makeRepoElem() function is creating an array of h1 element. And a list of repo name is rendered to the page.

Alt Text

I have added some styling to make the good appearance of the repos.

function InfiniteScrolling() {
  const [repoData, setRepoData] = useState([]);
  const [totalRepos, setTotalRepos] = useState(0);
  const [currentPage, setCurrentPage] = useState(1);

  const callBack = useCallback((node) => {

    if (node) console.log(node);
  })

  function fetchData() {
    (async () => {
      const data = await fetch(`https://api.github.com/users/praveen-me/repos?
page=${totalRepos ? currentPage : 1}`).then(response => response.json());

      if (currentPage < totalRepos / 30) {
        setCurrentPage(currentPage + 1);
      }

      setRepoData([...repoData, ...data]);
    })()
  }

  useEffect(() => {
    (async () => {
      const userData = await fetch("https://api.github.com/users/praveen-me")
.then(response => response.json());
      setTotalRepos(userData.public_repos);
    })()

    fetchData();
  }, [])

  return (
    <div className="App">
      {repoData.map((repo, index) => {
      return <h1 className="repo" key={repo.name + index}>{repo.name}</h1>
    })}
    </div>
  );

}
Enter fullscreen mode Exit fullscreen mode

Now, I use a useCallback and store a function in it. And I reference this useCallback to the last h1 element in the map. When the last element would be created, the stored function in the useCallback would be called.

Alt Text

Now, the question is where to use intersection observer API? We will follow the following steps...
1- We will create a new observer i.e. const observer = new IntersectionObserver(callback, options).
2- We will observe the last h1 element with this observer i.e. observer.observe(lastElement).

Whenever the last h1 element comes in the viewport, the callback will invoke.

function InfiniteScrolling() {
  const [repoData, setRepoData] = useState([]);
  const [totalRepos, setTotalRepos] = useState(0);
  const [currentPage, setCurrentPage] = useState(1);

  const observer = useRef();
  const callBack = useCallback((node) => {
    if (observer.current) observer.current.disconnect();

    observer.current = new IntersectionObserver(argus => {
      if (argus[0].isIntersecting) {
        if (currentPage < totalRepos / 30) {
          fetchData();
        }
      }
    })
    if (node) observer.current.observe(node);
  });

  function fetchData() {
    (async () => {
      const data = await fetch(`https://api.github.com/users/praveen-me/repos?
page=${totalRepos ? currentPage : 1}`).then(response => response.json());

      if (currentPage < totalRepos / 30) {
        setCurrentPage(currentPage + 1);
      }

      setRepoData([...repoData, ...data]);
    })()
  }

  useEffect(() => {
    (async () => {
      const userData = await fetch("https://api.github.com/users/praveen-me")
.then(response => response.json());
      setTotalRepos(userData.public_repos);
    })()

    fetchData();
  }, [])

  return (
    <div className="App">
      {repoData.map((repo, index) => {
      return (index === repoData.length - 1) ? (<h1 key={repo.name + index}
 className="repo" ref={callBack}>{repo.name}</h1>) : (<h1 className="repo" key={repo.name + index}>{repo.name}</h1>)
    })}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now, We need to define an observer and assign it to the useRef hook. And in the useCallback, we create a new intersection observer and assign it to the observer i.e. observer.current = new IntersectionObserver(callback, options). The callback receives a list of IntersectionObserverEntry objects and the observer. In IntersectionObserverEntry, there is a list of IntersectionObserverEntry.
We use argus[0].isIntersecting the entry of it to check whether the target element is intersecting with the viewport or not.
Now, we observe the target element(the element on which we have to apply the observer to invoke the callback) using this observer.current observer. We apply this observer on the last h1 element i.e. observer.current.observe(node) which we are getting by useCallback.

if (node) observer.current.observe(node);
Enter fullscreen mode Exit fullscreen mode

Whenever the callback is invoked, we need to disconnect the observer from the last target element and need to observe again new targeted element. i.e.

if (observer.current) observer.current.disconnect();
if (node) observer.current.observe(node);
Enter fullscreen mode Exit fullscreen mode

Now, whenever the last h1 element interacts with the viewport, the fetchData() function is invoked and a list of fetched repo is set to the repoData state.

if (argus[0].isIntersecting) {
        if (currentPage < totalRepos / 30) {
          fetchData();
        }
      }
Enter fullscreen mode Exit fullscreen mode

Alt Text

Yeee!!! We have done.
That's the way, you can use "Intersection Observer API" in react for infinite scrolling.

Top comments (1)

Collapse
 
ashish8796 profile image
Ashish Kumar Saini

I'll change it.