Infinite Scrolling Using Intersection Observer API
Published: 2021-03-31 08:41:45

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](https://dev-to-uploads.s3.amazonaws.com/i/wp1o3k10clzf113d28bp.gif)
    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] (https://developer.mozilla.org/en-US/docs/Web/API/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](https://dev-to-uploads.s3.amazonaws.com/i/vt3xr3w1q3tb31et351x.png)

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>
  );

}
```
`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](https://dev-to-uploads.s3.amazonaws.com/i/b7v3h4gx3xzosm46xvp9.gif)

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>
  );

}
```

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](https://dev-to-uploads.s3.amazonaws.com/i/ywwqck266ud5icpj5sou.png)

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>
  );
}
```

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] (https://developer.mozilla.org/en-US/docs/Web/API/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);
```

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);
```
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();
        }
      }
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jo20mwgana2tnwtgqtam.gif)

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