Building a React Infinite Scroller Component from Scratch

Krissanawat​ Kaewsanmuang
Bits and Pieces
Published in
5 min readDec 24, 2018

--

We all know the great content discovery experience where we can just scroll and scroll, discovering more items, people and photos. When building our web or mobile apps, we often want to give this great experience to our users.

In this tutorial, we are going to build a react infinite scroll with pure JavaScript and embed it into a ReactJS component.

With that component, we can achieve some awesome things:

  1. Fetching data from an API in JSON format
  2. Inject data into DOM element
  3. Use addEventListener to bind to the scroll event, and handle Paging using a common API
  4. Use internal state to store and append data
  5. Calculate the bottom of an expanding container

So, let’s dive in- step by step.

  • Tip: Use tools like Bit to turn your components into reusable Lego bricks. Create a collection, share your components, use them anywhere and build faster with your team. Give it a try.

Let’s get started

Use code sandbox to kick-start the project with create-react-app.

You’ll see the editor and the output browser.

Create a component structure used is the one below.

Fetching data from API and using it in the scrolling component

Import the necessary library.

import React from 'react'import fetch from 'isomorphic-fetch'

Use isomorphic-fetch to fetch the data easily.

Create a class Infinitescroll

class Infinitescroll extends React.Component {
render() {
return <div />;
}
}
export default Infinitescroll;

Include it in Index.js

import React from "react";
import ReactDOM from "react-dom";
import Infinitescroll from "./components/infinitescroll";
import "./styles.css";

Also, in the index.js.

function App() {
return (
<div className="container">
<h1>User</h1>
<Infinitescroll />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

The result should be:

Now, create a state variable to store data from API:

state = {
data: [],
per: 3,
page: 1,
total_pages: null,
};

Use a function to fetch data from an API

loadUser = () => {
const { per, page, data } = this.state;
const url = `https://reqres.in/api/users? per_page=${per}&page=${page}`;
fetch(url)
.then(response => response.json())
.then(json =>
this.setState({
data: son.data,
scrolling: false,
total_pages: json.total_pages
})
);
};

And render it:

render() {
return (
<ul>
{this.state.data.map(data => (
<li key={data.id}>
<div>
<div>
<img src={data.avatar} />
</div>
<div>{data.first_name}</div>
<div>{data.last_name}</div>
</div>
</li>
))}
</ul>
);
}

Rendering the component when the page loads- this can be achieved using componentWillMount .

componentWillMount() {
this.loadUser();
}

And the result:

Loading More Data

So, before loading the next page on-scroll, we need to load more data.

Add a new state variable called prevState to get next page value and call loadUser.

loadMore = () => {
this.setState(
prevState => ({
page: prevState.page + 1,
scrolling: true
}),
this.loadUser
);
};

Call this function on clicking Load More button.

<div>
<ul>
{this.state.data.map(data => (
<li key={data.id}>
<div>
<div>
<img src={data.avatar} />
</div>
<div>{data.first_name}</div>
<div>{data.last_name}</div>
</div>
</li>
))}
</ul>
<button
onClick={e => {
this.loadMore();
}}
>
Load More
</button>
</div>

The result…

So, this is pretty awesome but we have a problem: the old data gets replaced by the new one.

To solve this, append the new response to the old data.

fetch(url)
.then(response => response.json())
.then(json =>
this.setState({
data: [...data, ...json.data],
scrolling: false,
total_pages: json.total_pages
})
);

The result…

Well, it works just fine!

Trigger loadMore with scroll event

For the final part, trigger loadMore on a scroll event.

First, get last <li> with getQuerySelector. Calculate the element offset to get current page offset. If page offset is greater than the Offset of last item that means scrollbar is near the last offset. This should trigger loadMore.

handleScroll = () => { 
var lastLi = document.querySelector("ul.container > li:last-child");
var lastLiOffset = lastLi.offsetTop + lastLi.clientHeight;
var pageOffset = window.pageYOffset + window.innerHeight;
if (pageOffset > lastLiOffset) {
this.loadMore();
}
};

Bind scroll listeners to the scroll handler.

componentWillMount() {
this.loadUser();
this.scrollListener = window.addEventListener("scroll", e => {
this.handleScroll(e);
});
}

The result:

Great! That’s it. Your component is now ready! You can now use Bit to share it with your team, add it to your component collection and use it anywhere.

Quick Recap

In this post we learned how to implement a React infinite scroll component.

First, we learned how to fetch data from an API, and render it. With loadMore and handleScroll, we calculated the position of the scrollbar. Whenever the scrollbar is near the bottom of the window, fetch more data and append it to the existing data and render the component.

Hope you liked this post, and please feel free to comment and ask any questions! You can also leave me a few 👏 if you liked it. Cheers!

--

--

React Ninja l | Hire Me! | Contact: krissanawat101[at:()]gmail | Follow me on Twitter: @reactninja