Implementing Infinite Scroll Into a React Component

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.

? Alligator.io recommends ⤵

⚛️ Fullstack Advanced React & GraphQL by Wes Bos

ⓘ About this affiliate link


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.

For debouncing of the events (more on that in a bit), we’re going to use lodash:

To add superagent and lodash.debounce to your project via npm run:

$ npm install --save superagent lodash.debounce

Or via yarn:

$ yarn add superagent lodash.debounce

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.

When binding events, especially to scroll events, it’s good practice to debounce the events. Debouncing is when you only run a function once it a specified amount of time has passed since it was last called.

Debouncing improves performance for your user by limiting how often an event if fired and also helps take some strain off of any services you may be calling from the event handler.

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

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";
import debounce from "lodash.debounce";

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 = debounce(() => {
      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();
      }
    }, 100);
  }

  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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值