React Infinite Scroll Tutorial: With and Without a Library
Tags: javascript, webdev, react, tutorial
Published: 2020-07-09 10:25:55

This tutorial was originally published at <a href="https://www.devaradise.com/react-infinite-scroll-tutorial">https://www.devaradise.com/react-infinite-scroll-tutorial</a>

<!-- wp:paragraph -->
<p>Infinite scroll is a modern web &amp; application design concept that loads content continuously as the user scrolling down the page. It changes the function of pagination.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Implementing infinite scroll is suitable if you have a lot of data to load and you don't want users to click the page number to see more data. It boosts user experience in your application.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>As a developer, we can implement infinite scroll in any application, including a react application. React infinite scroll can be implemented in 2 ways, by manual implementation without a library and by using an infinite scroll library. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In this post, I will show and explain to you how to implement infinite scroll in a react project, with and without a library.  Both ways have their own advantages and drawbacks.</p>
<!-- /wp:paragraph -->

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/oo48vkmcqm19c7g4d7cb.jpg)

<!-- wp:paragraph -->
<p>Before we jump to the tutorial, make sure that you already know to initialize a react app using create-react-app boilerplate. Because I won't explain the basic react tutorial here. I assume you already understand that.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In this tutorial, we are going to use react functional components and hooks. We also using <a rel="noreferrer noopener" href="https://devaradise.com/lab/react/" target="_blank">react-lab</a> to host demo examples and <a rel="noreferrer noopener" href="https://www.devaradise.com/react-project-folder-structure" target="_blank">this project architecture</a> to manage the project files.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2>How to Implement Infinite Scroll Without Library</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Implementing a react infinite scroll without a library is the best if you want to make your react project as lightweight as possible. It also best if you're going to put some customization on it.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Personally, I will choose this method to implement an infinite scroll on my react app. I don't think it has many codes and logics to write. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>We only need some states, an event listener for scroll, an API calling service, and function to load data &amp; put some logics.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3>Create A Component</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Suppose that we will create a user list page that has infinite scroll implemented. So, we need a component to implement it like this.</p>
<!-- /wp:paragraph -->

```javascript
import React, { useState } from "react";

export default function InfiniteScrollNoLibrary() {

  const [userList, setUserList] = useState([]);
  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);
  const [noData, setNoData] = useState(false);

  return (
    <div>

      <div className="section">

        {userList.map((user, i) => 
          ( 
          <div className="box m-3 user" key={i}>
            <img src={user.avatar} alt={user.first_name}/>
            <div className="user-details">
              <strong>Email</strong>: {user.email}<br/> 
              <strong>First Name</strong>: {user.first_name}<br/> 
              <strong>Last Name</strong>: {user.last_name}<br/>
            </div>
          </div>
          )
        )}
        {loading ? <div className="text-center">loading data ...</div> : "" }
        {noData ? <div className="text-center">no data anymore ...</div> : "" }    
      </div>
    </div>
  );
}
```
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>I don't put business logic and event listener yet. Let me explain the states and markup first.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>To manually implement infinite scroll we need at least 4 states: </p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li><code>userList</code> to store an array of user data from API. The default is an empty array.</li><li><code>page</code> to count what page of user list to load. This helps us to not loading and adding the same data to the list. </li><li><code>loading</code> to give a loading state when calling the API.</li><li><code>noData</code> to give a no data state and stop API calling when there is no data anymore. </li></ul>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p>As you can see in the code above, <code>userList</code> state will be looped using <code>map</code> in the JSX markup. A ''loading ..." and "no data anymore …" text will also be added every time the <code>loading</code> and <code>noData</code> state has <code>true</code> value.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3>Create A Service for API Calling</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Before I add some logics to component, I create a service for calling user data first. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Actually, you can directly call an API in a component without creating a service. But, I personally prefer to separate it from the component. You can read the reason on my <a rel="noreferrer noopener" href="https://www.devaradise.com/react-project-folder-structure" target="_blank">react project structure article</a>.</p>
<!-- /wp:paragraph -->

```javascript
import axios from 'axios';

export default {

  getList: async function(page) {
    try {
      let url;
      if(page!=null & page > 1) {
        url ="https://reqres.in/api/users?per_page=2&page="+page;
      } else {
        url = "https://reqres.in/api/users?per_page=2";
      }
      const response = await axios.get(url);
      return response.data;
    } catch(error) {
      throw error;
    }
  }
}
```

<!-- wp:paragraph -->
<p>The getList function above accepts a <code>page</code> parameter to dynamically change URL string based on page number inserted. For dummy data, I use <a rel="noreferrer noopener" href="https://reqres.in" target="_blank">resreq.in</a> users API.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3>Adding Some Logic to Component</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>After creating a service, now we will use it in a component along with some logics. Have a look at the full component codes below. I will explain it after that.</p>
<!-- /wp:paragraph -->

```javascript
import React, { useState, useEffect } from "react";
import UserService from 'services/UserService';

export default function InfiniteScrollNoLibrary() {

  const [userList, setUserList] = useState([]);
  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);
  const [noData, setNoData] = useState(false);

  window.onscroll = () => {
    if (window.innerHeight + document.documentElement.scrollTop === document.documentElement.offsetHeight) {
      if(!noData) {
        loadUserList(page);
      }
    }
  }

  useEffect(() => {
    loadUserList(page);
  }, []);

  const loadUserList = (page) => {
    setLoading(true);
    setTimeout(() => {
      UserService.getList(page)
        .then((res) => {
          const newPage = page + 1;
          const newList = userList.concat(res.data);
          setUserList(newList);
          setPage(newPage);
          if(res.data.length===0)
            setNoData(true);
        })
        .catch((err) => {
          console.log(err);
        })
        .finally(() =>{
          setLoading(false);
        })
      }
    ,1500);
  }

  return (
    <div>

      <div className="section">

        {userList.map((user, i) => 
          ( 
          <div className="box m-3 user" key={i}>
            <img src={user.avatar} alt={user.first_name}/>
            <div className="user-details">
              <strong>Email</strong>: {user.email}<br/> 
              <strong>First Name</strong>: {user.first_name}<br/> 
              <strong>Last Name</strong>: {user.last_name}<br/>
            </div>
          </div>
          )
        )}
        {loading ?  <div className="text-center">loading data ...</div> : "" }
        {noData ? <div className="text-center">no data anymore ...</div> : "" }    
      </div>
    </div>
  );
}
```

<!-- wp:paragraph -->
<p>First, we import <code>UserService</code> and <code>useEffect</code> hook to the component. We will be use them later in the API calling function.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The most important codes in the component above are on the line 11 - 17.</p>
<!-- /wp:paragraph -->

```javascript
window.onscroll = () => {
    if (window.innerHeight + document.documentElement.scrollTop === document.documentElement.offsetHeight) {
      if(!noData) {
        loadUserList(page);
      }
    }
  }
```

<!-- wp:paragraph -->
<p>This is a function to listen when the user scrolls the page. Inside it, I put a logic: "If user scrolls to the bottom of the page, and <code>noData</code> state is false, then load the user list".</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>When a user just lands to the page and not scrolled yet, we load the user list inside the <code>useEffect</code> hook. So, the user data still loaded.</p>
<!-- /wp:paragraph -->

```javascript
useEffect(() => {
    loadUserList(page);
  }, []);
```

<!-- wp:paragraph -->
<p>Now, look into the <code>loadUserList</code> function.</p>
<!-- /wp:paragraph -->

```javascript
const loadUserList = (page) => {
    setLoading(true);
    setTimeout(() => {
      UserService.getList(page)
        .then((res) => {
          const newList = userList.concat(res.data);
          setUserList(newList);

          const newPage = page + 1;
          setPage(newPage);

          if(res.data.length===0)
            setNoData(true);
        })
        .catch((err) => {
          console.log(err);
        })
        .finally(() =>{
          setLoading(false);
        })
      }
    ,1500);
  }
```
<!-- wp:paragraph -->
<p>First, we set the loading state to <code>true</code> to show "loading ..." text when calling the API. I use setTimeout function here just to delay API calls so I can see the loading state. You don't have to use it in your codes.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In line 4, I call the getList function in UserService and pass <code>page</code> to it.  If the API request success, new user data from API will be added to the current user list (line 6 - 7).</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>We also need to set new <code>page</code> state for the next API calling when user scrolls again. You can see it on line 9 - 10.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Last, we create a condition to set <code>noData</code> State. If the API response is an empty array, it means that there is no more data to be loaded. So, we set the <code>noData</code> state to <code>true</code>.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>If the API request returns an error, catch it in the <code>catch</code> section. In this example, I just console.log it. And in <code>finally</code> section set the <code>loading</code> state to be <code>false</code> again because the request is over.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>That's it. Now you can practice it on your own.  To see the live demo for infinite scroll without library, you can click the link below.</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div class="links"><a class="link demo-link" href="https://www.devaradise.com/lab/react/infinite-scroll/no-library" target="_blank" rel="noreferrer noopener">Live Demo</a></div>
<!-- /wp:html -->

<!-- wp:heading -->
<h2>How to Implement Infinite Scroll With <code>react-infinite-scroller</code></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>If you don't want to implement react infinite scroll manually, you can still implement it using a library. There are a lot of libraries for React infinite scroll implementation out there.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Using a library for infinite scroll is the best if you want shorter code to write, and some options to easily customize it. Most of react infinite scroll libraries have more options and features than manual implementation I show you before.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In this tutorial, I use <code>react-infinite-scroller</code> as it is simple and easy to implement. Without further ado, let's see how to use it in your react project.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3>Install and Import react-infinite-scroller</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>First you should install react-infinite-scroller to your project using npm</p>
<!-- /wp:paragraph -->

```
npm i react-infinite-scroller
```

<!-- wp:paragraph -->
<p>To use it in a component, just import it like this.</p>
<!-- /wp:paragraph -->

```
import InfiniteScroll from 'react-infinite-scroller'
```

<!-- wp:heading {"level":3} -->
<h3>Using InfiniteScroll in a Component</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Here is the full component codes. I explain it below.</p>
<!-- /wp:paragraph -->

```javascript
import React, { useState } from 'react'
import InfiniteScroll  from 'react-infinite-scroller'
import UserService from 'services/UserService';

export default function InfiniteScrollerWithReactInfiniteScroller() {

  const [userList, setUserList] = useState([]);
  const [hasMoreItems, setHasMoreItems] = useState(true);

  const loadUserList = (page) => {
    setTimeout(() => {
      UserService.getList(page)
      .then((res) => {
        const newList = userList.concat(res.data);
        setUserList(newList);

        if(res.data.length===0) {
          setHasMoreItems(false);
        } else {
          setHasMoreItems(true);
        }
      })
      .catch((err) => {
        console.log(err);
      })
    
    }, 1500)
  }
  
  return (
    <div>
      <div className="section">
        <InfiniteScroll
          threshold={0}
          pageStart={0}
          loadMore={loadUserList}
          hasMore={hasMoreItems}
          loader={<div className="text-center">loading data ...</div>}>

            {userList.map((user, i) => 
              ( 
              <div className="box m-3 user" key={i}>
                <img src={user.avatar} alt={user.first_name}/>
                <div className="user-details">
                  <strong>Email</strong>: {user.email}<br/> 
                  <strong>First Name</strong>: {user.first_name}<br/> 
                  <strong>Last Name</strong>: {user.last_name}<br/>
                </div>
              </div>
              )
            )}
        </InfiniteScroll>
        {hasMoreItems ? "" : <div className="text-center">no data anymore ...</div> }    
      </div>
    </div>
  )
}
```

<!-- wp:paragraph -->
<p>As you can see, we have fewer states and logics to write if we use a library. We only need <code>userList</code> state to store our user data, and <code>hasMoreItems</code> to be passed to <code>&lt;InfiniteScroll/&gt;</code>. The <code>page</code> and <code>loading</code> state will be handled by <code>react-infinite-scroll</code>.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In the <code>loadUserList</code> function, we use the same UserService that I used before in the manual implementation. When the API request success, we only have to set a new user list (line 14-15) and set <code>hasMoreItems</code> state (line 17-21).</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Most of logic is handled by <code>&lt;InfiniteScroll/&gt;</code> that should wrap the <code>userList</code> looping.</p>
<!-- /wp:paragraph -->

```javascript
<InfiniteScroll
   threshold={0}
   pageStart={0}
   loadMore={loadUserList}
   hasMore={hasMoreItems}
   loader={<div className="text-center">loading data ...</div>}>
      {userList.map((user, i) => 
         ( 
         <div className="box m-3 user" key={i}>
            <img src={user.avatar} alt={user.first_name}/>
            <div className="user-details">
               <strong>Email</strong>: {user.email}<br/> 
               <strong>First Name</strong>: {user.first_name}<br/> 
               <strong>Last Name</strong>: {user.last_name}<br/>
            </div>
         </div>
         )
      )}
</InfiniteScroll>
```

<!-- wp:paragraph -->
<p>As you see, there is some attribute for <code>InfiniteScroll</code> i used above. Here is the explanation.</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li><code>threshold</code> is the distance between the bottom of the page and the bottom of the window's viewport that triggers the loading of new list -&nbsp;<em>Defaults to&nbsp;<code>250</code></em>. But i set it to 0.</li><li><code>pageStart</code> is the page number corresponding to the initial&nbsp;list, defaults to&nbsp;<code>0</code>&nbsp;which means that for the first loading,&nbsp;<code>loadMore</code>&nbsp;will be called with&nbsp;<code>1</code>.</li><li><code>loadMore(pageToLoad)</code> is called when the user scrolls down and we need to load a new list. The value should be a function. It will pass <code>page</code> number to the value.</li><li><code>hasMore</code> is a boolean stating whether there are more items to be loaded. Event listeners are removed if&nbsp;<code>false</code>.</li><li><code>loader</code> is loader element to be displayed while loading items - You can use&nbsp;InfiniteScroll.setDefaultLoader(loader);&nbsp;to set a default loader for all your&nbsp;<code>InfiniteScroll</code>&nbsp;components</li></ul>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p>To use more attributes, you can see <a rel="noreferrer nofollow noopener" href="https://github.com/noopkat/react-infinite-scroller" target="_blank">the documentation here</a>. And if you want see the live demo for infinite scroll with react-infinite-scoller, you can click the link below.</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div class="links"><a class="link demo-link" href="https://www.devaradise.com/lab/react/infinite-scroll/react-infinite-scroller" target="_blank" rel="noreferrer noopener">Live Demo</a></div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>That's all. I hope this useful for you.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Happy coding!</p>
<!-- /wp:paragraph -->