Lazy Loading — Lazy loading is an optimization technique for online content, Instead of loading the entire web page and rendering it to the user in one go as in bulk loading, the concept of lazy loading assists in loading only the required section and delays the remaining, until it is needed by the user.
Photo by Eduardo Dutra from Pexels
With the help of Infinite Scrolling and Lazy Loading, we can improve the performance and experience of our React applications. Imagine, you have around 10,000 data objects and each data object must have a separate component. If you render all the components at the same time, it would take a lot of time for the React application to render all the components at once and it would create a performance issue and a negative impact on user experience.
That’s where Infinite Scrolling and Lazy Loading comes handy. With infinite scrolling, we can limit the components to be rendered initially, and as we keep on scrolling the rest of the components can be rendered gradually. Now, there are certain things we can do to further enhance our performance and user experience. For example, the component also displays an image with a dynamic URL. Each component rendering image from the URL would take time. In the while, we could display a static image for all the components while Lazy Loading loads up the image in the background. For this purpose, we would create two components — List and Image. The list would implement Infinite Scrolling and Image would be lazily loaded in our List Component to enhance our application. Implementation is provided with Gist examples below.
Let’s start to implement Infinite Scrolling and Lazy Loading in our application. We would be creating a React application that will be calling an open-source API. The API has a query parameter page. Each page has an integer value and returns an array of 30 objects. Each object would have a name and an image field. The application would be displaying the name and the image of each person along with the hardcoded value of Architect and Engineer. The implementation is shown below.
For the purpose of implementing Infinite Scrolling, we would be using the Scroll Event Listener. Scroll Event Listener gives us access to the window and the document height, the current position of the scroll which would be sufficient enough to calculate the end of the page. Now, the application would start with page initialized to value 1, and as the user reaches the end of the page each time the value of the page would be incremented by +1. Now, as soon as the page loads up, an API call be would be made using **UseEffect(same as componentDidMount) **and the data would be fetched for page 1. And as the user would reach the end of the page, the scroll event listener would allow us to know the position of the scroll using the function handleScroll the page value would be incremented and the data would be fetched for **subsequent pages **and would be displayed to the user.
Infinite Scrolling would save us a lot of time to make the API call for the entire page at once and rendering of all the data at once improving our performance and user experience.
Now, for the purpose of implementing Lazy Loading, we would be using Suspense and Lazy which are a new addition to the React. So, let’s learn more about Suspense and Lazy. Suspense for Data Fetching is a new feature that lets you also use to declaratively “wait” for anything else, including data. This page focuses on the data fetching use case, but it can also wait for images, scripts, or other asynchronous work. Suspense lets your components “wait” for something before they can render. In our application, we would be using Suspense to wait for the image to be rendered and in the meanwhile, we would be displaying a static generic image for all the components to the user using the fallback property and the soon as the image is loaded our fallback component would be unmounted and the Image Component would be mounted.
Lazy Loading would be a boost to our user experience. Instead of displaying nothing, we would be displaying a generic image till image loads up, and soon as the image gets loaded, it would be shown to the user without any change on the UI side.
import React, { useState, useEffect, Suspense } from "react";
import imageFile from "./img_avatar.png";
import "./list.css";
const ImageComponent = React.lazy(() => import("../image/image"));
const List = () => {
const [listItems, setListItems] = useState([]);
const [isFetching, setIsFetching] = useState(false);
const [page, setPage] = useState(1);
useEffect(() => {
fetchData();
window.addEventListener("scroll", handleScroll);
}, []);
const handleScroll = () => {
if (
Math.ceil(window.innerHeight + document.documentElement.scrollTop) !==
document.documentElement.offsetHeight ||
isFetching
)
return;
setIsFetching(true);
console.log(isFetching);
};
const fetchData = async () => {
setTimeout(async () => {
const result = await fetch(`https://picsum.photos/v2/list?page=${page}`);
const data = await result.json();
setPage(page + 1);
setListItems(() => {
return [...listItems, ...data];
});
}, 1000);
};
useEffect(() => {
if (!isFetching) return;
fetchMoreListItems();
}, [isFetching]);
const fetchMoreListItems = () => {
fetchData();
setIsFetching(false);
};
return (
<>
{listItems.map((listItem) => (
<div className="card" key={listItem.id}>
<Suspense
fallback={
<img src={imageFile} alt="Avatar" style={{ width: "50%" }} />
}
>
<ImageComponent src={listItem.download_url} />
</Suspense>
<div className="container">
<h4>
<b>{listItem.author}</b>
</h4>
<p>Architect and Engineer</p>
</div>
</div>
))}
{isFetching && <h1>Fetching more list items...</h1>}
</>
);
};
export default List;
And the Image Component would look like this -
import React from "react";
const ImageComponent = ({ src }) => {
return <img src={src} alt="Avatar" style={{ width: "50%" }} />;
};
export default ImageComponent;
So let’s break the code -
-
Since we are using hooks, we use the useState and useEffect component. Suspense is the latest addition to React to create lazy loading. You can read it more about it here — https://reactjs.org/docs/code-splitting.html
-
Now, we are creating a simple Image Component which will be lazily loaded using React.lazy
-
Now we will be using 3 states — **listItems **— Array of data being fetched from the server isfetching — Boolean when we reach the end of the page to be set to true page — Page number to get the data from the server increases by +1 when isfetching is true and initially set to 1.
-
useEffect() — Just like componentDidMount would run once to fetch the data from the server for page 1 and adding event listener scroll binding it to function handleScroll.
-
handleScroll() — In handleScroll, we use a simple formula Math.ceil(window.innerHeight…..) to determine when the user enters the end of scrolling or the page. Math.ceil is quite handy when the user uses the zoom feature in modern browsers and set isFetching to true.
-
Now, we use useEffect to determine any change in** isFetching **and as soon as isFetching is changed and is true we call function fetchMoreData.
-
And fetchMoreData calls fetchData which has a setTimeOut function to merely show the transition and nothing more. fetchData increases the page by +1 and then calls the API accordingly and pushes the data into the listItems array.
-
The return method is simple with the exception of Suspense. The fallback in Suspense would be rendered initially and as soon as ImageComponent is loaded, the fallback would be unmounted and **ImageComponent **would be rendered.
Complete Github Code: https://github.com/ANUPAMCHAUDHARY1117/React-Infinte-Scroll-And-lazy-Loading