Ogundipe Samuel software engineer and technical writer

Infinite scroll techniques in React

4 min read 1307

Introduction

Infinite scrolling is a web design technique that loads content continuously as the user scrolls down the page, eliminating the need for pagination.

Content is often loaded asynchronously by making a request to the server. Often times this can improve the user experience on a website.

But not always. Sometimes it’s terrible.

Infinite scrolling technically requires adding a scroll event listener to the window object or a certain div. This determines when the scroll has reached the bottom of the div and then performs actions accordingly.

In this tutorial, I will explain two methods of implementing infinite scroll in React:

  1. The first method describes implementing everything from the ground up
  2. The second method uses an already available infinite scroll library/component

A basic understanding of React is needed to follow through this tutorial.

Implementing from the ground up

As mentioned earlier, infinite scroll is about attaching event listeners to DOM elements while watching for when the scrollbar hits the bottom of the div.

Look at the render function of this Component below:

render() {
return (
  <div
    className="App"
    ref="myscroll"
    style={{ height: "420px", overflow: "auto" }}
  >
    <header className="App-header">
      <h1 className="App-title">Welcome to React</h1>
    </header>
    <ul>
    </ul>
  </div>
);
}

Say you want to load more li items into the ul tag each time the div with the class App gets to the end of the div, how do you tackle this problem?

First, notice that there is a reference to the div called myscroll which makes it possible to access the element in React using this.refs.myscroll.

We made a custom demo for .
No really. Click here to check it out.

Declare an initial state in the constructor:

constructor(props) {
    super(props);
    this.state = {
      items: 20,
      loading: false
    };
}

Here, you declared two states: items and loading. The items hold the number of items available to be shown as li tags while the loading state will show when the infinite loader is fetching more items.

Next, create a function that renders all items:

showItems() {
    var items = [];
    for (var i = 0; i < this.state.items; i++) {
      items.push(<li key={i}>Item {i}</li>);
    }
    return items;
}

This function loops through the number of items present and create a li tag showing the item number.

Now, update your render function to display these items:

render() {
    return (
      <div
        className="App"
        ref="myscroll"
        style={{ height: "420px", overflow: "auto" }}
      >
        <header className="App-header">
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <ul>
          {this.showItems()}
        </ul>
        {this.state.loading
          ? <p className="App-intro">
          loading ...
        </p>
          : ""}
      </div>
    );
}

What you have is a normal component that shows a couple li‘s showing the items number. How do you add infinite scroll to this component? remember the ref called myscroll? You get to use it now. Define the ComponentWillMount method:

componentDidMount() {
    // Detect when scrolled to bottom.
    this.refs.myscroll.addEventListener("scroll", () => {
      if (
        this.refs.myscroll.scrollTop + this.refs.myscroll.clientHeight >=
        this.refs.myscroll.scrollHeight
      ) {
        this.loadMore();
      }
    });
}

In the method above, a scroll listener was added to the myscroll ref which references the div being targeted. Here you used the scrollTop property of the element to get the scroll position (which is relative to the top of the window) and then added it to the clientHeight property (the height of the document).

Next, a check is made to see if the sum of those two properties is greater than or equal to the height of the scrollbar. If the assumption is true, then the bottom of the div has been reached. A new function called loadMore (Which will be created next) is then fired.

Here is what the loadMore function looks like:

loadMore() {
    this.setState({ loading: true });
    setTimeout(() => {
        this.setState({ items: this.state.items + 20, loading: false });
    }, 2000);
}

In this method, the loading is first set to true, so the loading div is shown. Next, a timeout function is called, after which the items are increased by 20 and the loading state is set back to false.

The reason for the timeout, however, is to cause a little delay. In your application, you probably want to make a fetch or axios call to your server and then change state.

But no matter your use case, the concept remains the same.

Here is what the final component should look like:

import React, { Component } from "react";
    import logo from "./logo.svg";
    import "./App.css";
    
    class App extends Component {
      constructor(props) {
        super(props);
        this.state = {
          items: 20,
          loading: false
        };
      }
      componentDidMount() {
        // Detect when scrolled to bottom.
        this.refs.myscroll.addEventListener("scroll", () => {
          if (
            this.refs.myscroll.scrollTop + this.refs.myscroll.clientHeight >=
            this.refs.myscroll.scrollHeight
          ) {
            this.loadMore();
          }
        });
      }
    
      showItems() {
        var items = [];
        for (var i = 0; i < this.state.items; i++) {
          items.push(<li key={i}>Item {i}</li>);
        }
        return items;
      }
    
      loadMore() {
        this.setState({ loading: true });
        setTimeout(() => {
          this.setState({ items: this.state.items + 20, loading: false });
        }, 2000);
      }
    
      render() {
        return (
          <div
            className="App"
            ref="myscroll"
            style={{ height: "420px", overflow: "auto" }}
          >
            <header className="App-header">
              <img src={logo} className="App-logo" alt="logo" />
              <h1 className="App-title">Welcome to React</h1>
            </header>
            <ul>
              {this.showItems()}
            </ul>
            {this.state.loading
              ? <p className="App-intro">
                  loading ...
                </p>
              : ""}
    
          </div>
        );
      }
    }
    
    export default App;

Using an infinite scroll library

While the first method showed how relatively easy it is to implement infinite scroll in React applications, you might not be so content to implement event listeners yourself.

You might also just want a solution that works without all the fuss because you’re kind of lazy (or, more likely, pressed for time).

That’s okay, I’ve got you covered.

For this purpose, you can use React-infinite-scroller , available here. Here’s an example of how it can be used:

import React, { Component } from "react";
    import logo from "./logo.svg";
    import "./App.css";
    
    import InfiniteScroll from "react-infinite-scroller";
    
    class Scroll2 extends Component {
      constructor(props) {
        super(props);
        this.state = {
          items: 20,
          hasMoreItems: true
        };
      }
    
      showItems() {
        var items = [];
        for (var i = 0; i < this.state.items; i++) {
          items.push(<li key={i}> Item {i} </li>);
        }
        return items;
      }
    
      loadMore() {
        if(this.state.items===200){
          
          this.setState({ hasMoreItems: false});
        }else{
            setTimeout(() => {
            this.setState({ items: this.state.items + 20});
        }, 2000);
        }
        
      }
    
      render() {
        return (
          <div className="App">
            <header className="App-header">
              <img src={logo} className="App-logo" alt="logo" />
              <h1 className="App-title"> Welcome to React </h1>{" "}
            </header>
    
            <div style={{height:'200px', overflow:'auto'}}>
              <InfiniteScroll
                loadMore={this.loadMore.bind(this)}
                hasMore={this.state.hasMoreItems}
                loader={<div className="loader"> Loading... </div>}
                useWindow={false}
              >
                {this.showItems()}{" "}
              </InfiniteScroll>{" "}
            </div>{" "}
          </div>
        );
      }
    }
    
    export default Scroll2;

Looking at the code above, notice it looks similar to the previous method? What are the main differences of this code from the one in the previous method?

  • Here, there is no event listener being attached
  • Here, no reference to the div was made, as it wasn’t needed
  • No loading state was defined. Instead, there is hasMoreItems which is used to tell the Infinite scroll component to detach the event listener
  • An alteration to the loadMore function, which sets the hasMoreItems state to false once the items his 200

Conclusion

And there you have it, two different methods that allow you to implement infinite scroll in React applications.

For those of you who want to get involved in writing the event listeners yourself, you have seen how simple and easy it is.

For those of you who do not want to get involved in attaching the event listeners, there’s a method that will work for you too.

Have any comments or observations? That’s what the comment section is for.

You come here a lot! We hope you enjoy the LogRocket blog. Could you fill out a survey about what you want us to write about?

    Which of these topics are you most interested in?
    ReactVueAngularNew frameworks
    Do you spend a lot of time reproducing errors in your apps?
    YesNo
    Which, if any, do you think would help you reproduce errors more effectively?
    A solution to see exactly what a user did to trigger an errorProactive monitoring which automatically surfaces issuesHaving a support team triage issues more efficiently
    Thanks! Interested to hear how LogRocket can improve your bug fixing processes? Leave your email:

    Full visibility into production React apps

    Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

    LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

    The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

    Modernize how you debug your React apps — .

    Ogundipe Samuel software engineer and technical writer

    One Reply to “Infinite scroll techniques in React”

    1. Hi,
      Thanks,

      I followed you & implemented the infinite scroll. I have been facing this issue.
      I have set page size 30, So for each API response I receive the 30 data. The problem is, when I open the application in big screen like TV/projector, Initial API call loads the 30 data & no scroll appear due to large screen. But I have 1000 data in Database.

      I told team, We could increase the initial page size 100, then they asked suppose after increased size What If the scroll doesn’t appear on window. More over, they want to keep page size 30 only, do not want to change.

      How to solve when I open the application in big screen, load the data until scroll appear?

    Leave a Reply