Implementing Infinite Scroll Into a React Component

joshtronic

In our face-paced modern society, who has time to click through pages and pages of content? “Not I,” said the web developer. In a world full of shortcuts, swipes and other gestures, the most efficient way to get through pages of content is the infinite scroll.

While not a new concept, the idea of infinite scroll is still somewhat controversial. Like most things, it has a time and a place in modern web design when properly implemented.

For anybody unfamiliar, infinite scroll is the concept of new content being loaded as you scroll down a page. When you get to the bottom of the content, the site automatically loads new content and appends it to the bottom.

Infinite scroll may not be ideal for all types of content, it is especially useful of feeds of data that an end-user would most probably want to page through quickly.

A perfect use case, and one you may already be familiar with is Instagram. You are presented with a feed of images and as you scroll down, more images keep showing up. Over and over and over until they run out of content to give you.

This article will teach you how to implement the infinite scroll concept into a React component and uses the Random User Generator to load a list of users from.


Before we begin, we need to make sure we have the proper dependencies in our project. To load data from the Random User Generator we are going to use superagent.

To add superagent to your project via npm run:

$ npm install --save superagent

Or via yarn:

$ yarn add superagent

The crux of our infinite scroll component is going to be an onscroll event that will check to see if the user has scrolled to the bottom of the page. Upon reaching the bottom of the page, our event will attempt to load additional content:

window.onscroll = () => {
  if (
    window.innerHeight + document.documentElement.scrollTop
    === document.documentElement.offsetHeight
  ) {
    // Do awesome stuff like loading more content!
  }
};

The data that has been loaded will be appended to an array in the component’s state and will be iterated through in the component’s render method.

All good things come to an end. For demonstration purposes our component will eventually stop loading new content and display a message that it's reached the end and there is no additional content.


Now that we understand the logic flow that’s necessary to implement infinite scroll, let’s dive into our component:

import React, { Component, Fragment } from "react";
import { render } from "react-dom";
import request from "superagent";

class InfiniteUsers extends Component {
  constructor(props) {
    super(props);

    // Sets up our initial state
    this.state = {
      error: false,
      hasMore: true,
      isLoading: false,
      users: [],
    };

    // Binds our scroll event handler
    window.onscroll = () => {
      const {
        loadUsers,
        state: {
          error,
          isLoading,
          hasMore,
        },
      } = this;

      // Bails early if:
      // * there's an error
      // * it's already loading
      // * there's nothing left to load
      if (error || isLoading || !hasMore) return;

      // Checks that the page has scrolled to the bottom
      if (
        window.innerHeight + document.documentElement.scrollTop
        === document.documentElement.offsetHeight
      ) {
        loadUsers();
      }
    };
  }

  componentWillMount() {
    // Loads some users on initial load
    this.loadUsers();
  }

  loadUsers = () => {
    this.setState({ isLoading: true }, () => {
      request
        .get('https://randomuser.me/api/?results=10')
        .then((results) => {
          // Creates a massaged array of user data
          const nextUsers = results.body.results.map(user => ({
            email: user.email,
            name: Object.values(user.name).join(' '),
            photo: user.picture.medium,
            username: user.login.username,
            uuid: user.login.uuid,
          }));

          // Merges the next users into our existing users
          this.setState({
            // Note: Depending on the API you're using, this value may
            // be returned as part of the payload to indicate that there
            // is no additional data to be loaded
            hasMore: (this.state.users.length < 100),
            isLoading: false,
            users: [
              ...this.state.users,
              ...nextUsers,
            ],
          });
        })
        .catch((err) => {
          this.setState({
            error: err.message,
            isLoading: false,
           });
        })
    });
  }

  render() {
    const {
      error,
      hasMore,
      isLoading,
      users,
    } = this.state;

    return (
      <div>
        <h1>Infinite Users!</h1>
        <p>Scroll down to load more!!</p>
        {users.map(user => (
          <Fragment key={user.username}>
            <hr />
            <div style={{ display: 'flex' }}>
              <img
                alt={user.username}
                src={user.photo}
                style={{
                  borderRadius: '50%',
                  height: 72,
                  marginRight: 20,
                  width: 72,
                }}
              />
              <div>
                <h2 style={{ marginTop: 0 }}>
                  @{user.username}
                </h2>
                <p>Name: {user.name}</p>
                <p>Email: {user.email}</p>
              </div>
            </div>
          </Fragment>
        ))}
        <hr />
        {error &&
          <div style={{ color: '#900' }}>
            {error}
          </div>
        }
        {isLoading &&
          <div>Loading...</div>
        }
        {!hasMore &&
          <div>You did it! You reached the end!</div>
        }
      </div>
    );
  }
}

const container = document.createElement("div");
document.body.appendChild(container);
render(<InfiniteUsers />, container);

There’s really not much to it!

The component manages a few status flags and list of data in it’s state, the onscroll event does most of the heavy lifting and the render method brings it to life on your screen!

We’re also making use of setState with a callback function passed-in as the second argument. The initial call to setState in our loadUsers method sets the value of loading to true and then in the callback function we load some users and call setState again to append our new users to the users already in the state.


I hope you’ve found this article on implementing infinite scroll in React informative.

If interested, you can find a working demo of this component over on CodeSandbox.

Enjoy! 💥

  Tweet It

🕵 Search Results

🔎 Searching...