Infinite scrolling progressively loads more content as the user scrolls down the page. This creates a smooth and continuous experience, keeping users engaged without the interruption of page reloads.
In the dynamic world of web development, user experience is paramount. One way to enhance this experience is by implementing infinite scrolling, a feature that allows users to seamlessly load more content as they scroll down the page. In this blog post, we'll dive into creating the infinite scroll functionality in a React application, unlocking a world of possibilities for a smoother and more engaging user interface.
Understanding Infinite Scroll
Before we jump into the code, let's understand the concept of infinite scrolling. Instead of loading an entire page's worth of content at once, infinite scrolling progressively loads more content as the user scrolls down the page. This creates a smooth and continuous experience, keeping users engaged without the interruption of page reloads.
Setting Up the Project
Let's start by setting up a new React project with Typescript. Open your terminal and run the following commands:
npx create-react-app infinite-scroll-demo --template typescript
cd infinite-scroll-demo
This will create a new React project with Typescript support.
Create useInfiniteScroll Hook
Create a hook that will check scroll behaviour and load the data accordingly. For simplicity, let's call it useInfiniteScroll
.
// useInfiniteScroll.ts
import React from "react";
export interface useInfiniteScrollProps {
ref?: React.RefObject<any>;
option?: Omit<IntersectionObserverInit, "root">;
onScrollEnd?: () => Promise<void> | void;
}
export const useInfiniteScroll = ({
ref,
onScrollEnd,
option = {}
}: useInfiniteScrollProps) => {
const isCalled = React.useRef<IntersectionObserverEntry | null>(null);
const observer = React.useRef<IntersectionObserver | null>(null);
React.useEffect(() => {
observer.current = new IntersectionObserver(
(entries, observer) => {
if (
entries[0].isIntersecting &&
!isCalled.current?.target.isSameNode(entries[0].target)
) {
isCalled.current = entries[0];
void onScrollEnd?.();
observer.unobserve(entries[0].target);
observer.observe(ref?.current.lastChild as Element);
}
},
{
root: ref?.current,
...option
}
);
if (ref?.current) {
observer.current?.observe(ref?.current.lastChild as Element);
}
}, [onScrollEnd, option, ref]);
return observer.current?.disconnect;
};
In this hook, we are accepting three parameters,
ref
— reference of your container to determine the area of scrolling.onScrollEnd
— an event that will fire when you reach the end of scrolling.option
— will accept the configuration of the Intersection observer.
Create InfiniteScroll component
let's use this useInfiniteScroll
hook and create a InfiniteScroll
component.
// InfiniteScroll.tsx
import React, { useEffect } from "react";
import { useInfiniteScroll } from "./useInfiniteScroll";
export function InfiniteScroll() {
const ref = React.useRef<HTMLDivElement>(null);
const [isLoading, setIsLoading] = React.useState(false);
const [products, setProducts] = React.useState([
"gpjaznf",
"hefztsugpwcdbxanvm",
"kodhqlctubmvnigxasjwrpfzey",
"utkigpjnlrymdqabvoexs",
"cblkexdqy",
"kdfzelrpnvctuyhmxb",
"dybkzlefotpvwxmshacq",
"dpmhcobxrqsgwfkvenlaiytjz",
"hnerbpyxlagiocsmkqdvwf",
"falqhvpbxykemjcsdtuzrogwni",
"wunj",
"sxdwmcobfr",
"edcqmhyguwslkpnbrtvxajifoz",
"gmkh",
"cwxfilgjyku",
"myjdcqziostvplfuwh",
"cbptdeofihn",
"hg",
"mabxrozvlncpdjqesutfg",
"huakjpmieo"
]);
const unsubscribe = useInfiniteScroll({
ref,
async onScrollEnd() {
setIsLoading(true);
await setTimeout(() => {
setIsLoading(false);
setProducts((prev) => [
...prev,
...[...Array(20)].map(() => {
const randomStr = "abcdefghijklmnopqrstuvwxyz"
.split("")
.sort(() => 0.5 - Math.random())
.join("");
return randomStr.slice(0, Math.random() * 26 + 2);
})
]);
}, 3000);
},
option: {
threshold: 1
}
});
useEffect(() => {
return () => {
if (unsubscribe) {
unsubscribe();
}
};
}, [unsubscribe]);
return (
<div
className="scroll_container"
ref={ref}
>
{products.map((el, id) => (
<p key={id}>{el}</p>
))}
{isLoading && <h5>Loading....</h5>}
</div>
);
};
Adding in DOM tree
Let's add our component to our src/App.ts
file.
// src/App.tsx
import React from "react";
import { InfiniteScroll } from "./InfiniteScroll"
import "./styles.css";
export default function App() {
return (
<div className="App">
<h3>Infinity scroll</h3>
<InfiniteScroll/>
</div>
);
};
Styling
Let's add some basic styling to make our component visually appealing.
/* styles.css */
.App {
font-family: sans-serif;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
}
.scroll_container {
height: 400px;
width: 100%;
background-color: beige;
overflow: scroll;
}
Feel free to style your components, add loading spinners, or customize the UI based on your project requirements. The provided code is a foundation that you can build upon to create a seamless infinite scrolling experience in your React application.
- Codesandbox: https://codesandbox.io/s/infinity-scroll-qphg93
- Github: https://github.com/saikat-samanta/infinity-scroll-react-example
Conclusion
Implementing infinite scroll in a React application adds a touch of sophistication and user-friendliness, transforming the way users interact with your content. By breaking free from the constraints of traditional pagination, you can provide a more engaging and fluid experience, ultimately enhancing the overall user satisfaction. Experiment with the code, explore additional features, and let your creativity flourish as you create infinite possibilities for your React applications. Happy coding!