Any application, whether it’s a web, desktop, or mobile app, has tons of records in terms of data. A user may want to access this data in one place for any number of reasons related to the product, items, flights, trains, homes, and so on.
For such functionality, it is difficult to load all records at once due to overall performance issues, so we need an alternative. One alternative is called an infinite scroll.
Infinite scroll is a mechanism that shows data based on an endless scroll event and loads data only as needed to avoid critical performance issues.
Basically, the infinite scroll method is pretty handy compared to pagination, where a user must click on the page number every time they want to load the next page’s data.
The infinite scrolling mechanism is only advisable when we want to continuously load data that belongs to the same level of the hierarchy. Otherwise, we can opt for other alternatives.
The infinite scrolling feature may seem like an elegant replacement for pagination. However, it is not the answer for all websites. Infinite scrolling is probably not for you if site visitors want to achieve specific types of goal-oriented activities, such as when they need to backtrack or find specific information quickly without struggling too much.
In this guide, we will implement an infinite scroll using custom logic. Let’s jump into an example.
In React, we have two choices to develop an infinite scroll.
Here in this guide, we will develop a simple custom infinite scrolling mechanism that helps us to load data based on a scroll event.
Before getting started, we need data to load continuously, so for that, we will use a dummy/mock API platform that provides us the fake data using their official API. The URL we are going to use is given below.
https://jsonplaceholder.typicode.com/photos
To call the API, we should have Axios installed in our app, which is used to make an HTTP request to fetch or save the data from our application. It is a third-party library, so we can install it using the below npm
command.
1npm install axios
Let’s create a new child component called ScrollComponent.jsx
and create a state object like this.
1import React, { Component } from "react";
2import axios from "axios";
3
4class ScrollComponent extends Component {
5 constructor() {
6 super();
7 this.state = {
8 photos: [],
9 loading: false,
10 page: 0,
11 prevY: 0
12 };
13 }
14
15 render() {
16 return (
17 <div className="container">
18 </div>
19 );
20 }
21}
22
23export default ScrollComponent;
In our scroll component, we have created a few state values to store the photos from the API response and other state values to manage page properties and loading behavior.
Now, we have to create one function that contains the source code used to get the API data from the fake API using the Axios package. Let’s create a reusable function like this.
1 getPhotos(page) {
2 this.setState({ loading: true });
3 axios
4 .get(
5 `https://jsonplaceholder.typicode.com/photos?_page=${page}&_limit=10`
6 )
7 .then(res => {
8 this.setState({ photos: [...this.state.photos, ...res.data] });
9 this.setState({ loading: false });
10 });
11 }
In this function, we have set the loading state value to true
, which denotes the data is being loaded from the API. We have used Axios along with the API URL, which fetches the data from the server.
After getting the response based on the request, we need to store the data into a separate array called photos
that we have created earlier in this component.
If you notice, we have combined the previous and current data, just because we will call this function continuously based on the scroll event, and it will append the response into the photos array.
After creating the function, we have to call it once the component has loaded, hence we can call it from the componentDidMount ()
function like this.
1 componentDidMount() {
2 this.getPhotos(this.state.page);
3 }
We have configured the basic API function from where we will call the API. We have also called the getPhotos()
function from the componentDidMount()
lifecycle hook, but it will be enough to develop the infinite scroll.
Now, we have to listen to each scroll event behavior, and based on the scroll event, the page number will be updated in order to fetch the new data from the API.
We will use the intersection observer
, which does two things:
Let’s update the componentDidMount()
hooks like this.
1componentDidMount() {
2 this.getPhotos(this.state.page);
3
4 var options = {
5 root: null,
6 rootMargin: "0px",
7 threshold: 1.0
8 };
9
10 this.observer = new IntersectionObserver(
11 this.handleObserver.bind(this),
12 options
13 );
14 this.observer.observe(this.loadingRef);
15 }
In this hook function, we have created a few options:
Apart from these three options, we have an actual intersection observer function, which contains two different parameters:
By using the intersection observer, we can listen for any changes from the target element, and if there are any changes, then the callback function will be triggered.
Now let’s implement the callback trigger function, which looks like this.
1handleObserver(entities, observer) {
2 const y = entities[0].boundingClientRect.y;
3 if (this.state.prevY > y) {
4 const lastPhoto = this.state.photos[this.state.photos.length - 1];
5 const curPage = lastPhoto.albumId;
6 this.getPhotos(curPage);
7 this.setState({ page: curPage });
8 }
9 this.setState({ prevY: y });
10 }
In this function, we will configure the page number based on the target element being scrolled. We will hold the last page being scrolled, and based on the current page, the fetch API comes into the picture and fetches the latest data from the server.
As soon as we scroll the data, the current page will be updated into the state along with the previous page number.
This is what we wanted to develop so far in this guide, but it’s not the end at all. We left out a critical step to show the photos and render them into the DOM.
Replace the render()
function with the given source code.
1render() {
2
3 // Additional css
4 const loadingCSS = {
5 height: "100px",
6 margin: "30px"
7 };
8
9 // To change the loading icon behavior
10 const loadingTextCSS = { display: this.state.loading ? "block" : "none" };
11
12 return (
13 <div className="container">
14 <div style={{ minHeight: "800px" }}>
15 {this.state.photos.map(user => (
16 <img src={user.url} height="100px" width="200px" />
17 ))}
18 </div>
19 <div
20 ref={loadingRef => (this.loadingRef = loadingRef)}
21 style={loadingCSS}
22 >
23 <span style={loadingTextCSS}>Loading...</span>
24 </div>
25 </div>
26 );
27 }
The render function contains a different configuration, which is explained below.
Create some additional styles for the icon that displays while loading the content like this.
1const loadingCSS = {
2 height: "100px",
3 margin: "30px"
4};
Another CSS style used to change the class property for the loading icon is called loadingTextCSS
and looks like this.
1const loadingTextCSS = { display: this.state.loading ? "block" : "none" };
At last, into the return function, we have used our state array, called photos
, which contains the list of photos coming from the API response.
1<div style={{ minHeight: "800px" }}>
2 {this.state.photos.map(user => (
3 <img src={user.url} height="100px" width="200px" />
4 ))}
5</div>
Along with the photos list, we have configured the loading icon based on the loading reference so that as soon as we scroll down to the target element, it shows the loading icon. When the loading process is completed, the loading icon will disappear.
1<div
2 ref={loadingRef => (this.loadingRef = loadingRef)}
3 style={loadingCSS}
4>
5 <span style={loadingTextCSS}>Loading...</span>
6</div>
We are done with our HTML content. Now if we run this example, we will get the initial 10 records per page because we have configured the additional parameters with the API like this.
https://jsonplaceholder.typicode.com/photos?_page=${page}&_limit=10
So when the user scrolls down the page, the page number will be updated frequently; but the page limit stays as it is, so 10 records at a time will be added into the array and render into the DOM.
This is the complete process for using an intersection observer to get the position of the target element and return the movement into the DOM.
In this guide, we have developed an infinite scroll using custom implementation, but we could also use third-party libraries. A few of them include:
Many more packages are available, so choose wisely based on your project requirements.
Infinite scrolling is becoming a popular way to load data based on a scroll event that loads data continuously whenever the user reaches the bottom of the page.
In this guide, we have learned a custom approach for implementing infinite scroll in ReactJS, but we can also use several third-party libraries to achieve the same result. I hope this guide will be helpful to you someday. If you have any queries, feel free to reach out at Codealphabet.