Infinite scroll component in React and NextJS
Mileta Dulovic
Infinite scroll is a great way to load huge lists as it provides a great UX for user when compared to traditional pagination, because user doesn't need to click page number to load more items.
Component
Component can be integrated in two ways
-
If your app uses document to scroll, then you just need to wrap your list with
InfiniteScroll
and the component will do everything for you -
If you have a div that is scrollable, you need to assign
ID
to that div<div id="wrapper"/>
and then passscrollElement
prop toInfiniteScroll
-<InfiniteScroll scrollElement="#wrapper"/>
Logic
First we need to check for user's scroll position, and load more items. We do this by getting how much user scrolled from the top. Then we calculate how much user has until the end of the page, and we load more items.
Code
import { useState, useEffect, cloneElement } from "react";
// onBottomReach: optional callback when user reaches bottom
// scrollThreshold: load more items if user has at least 1000 pixels before the end
// scrollElement: ID of the element that is scrollable. Document is default
// perPage: items to load per page
const InfiniteScroll = ({
onBottomReach,
scrollThreshold = 1000,
children,
scrollElement,
perPage = 10,
}) => {
const [page, setPage] = useState(1);
const handleScroll = (e) => {
const target = scrollElement
? document.getElementById(scrollElement)
: e?.target?.scrollingElement;
// detects if user is at the bottom while taking scroll threshold into the account
const bottom =
target.scrollTop + target.clientHeight >=
target.scrollHeight - scrollThreshold;
if (bottom) {
// checks if onBottomReach is passed to the component and calls it
if (typeof onBottomReach === "function") {
onBottomReach();
} else {
setPage((prevState) => prevState + 1);
}
}
};
useEffect(() => {
// adds listener when component mounts, while making sure that the enviorement is not the server
if (typeof window !== "undefined") {
document?.addEventListener("scroll", handleScroll, {
passive: true,
capture: true,
});
}
// clear the listener to avoind memory leaks
return () =>
document?.removeEventListener("scroll", handleScroll, { capture: true });
}, []);
return <>{children?.slice(0, page * perPage)?.map((item) => item)}</>;
};
export { InfiniteScroll };
More in this category
How to cancel requests with Apollo GraphQL
If you need to cancel requests with Apollo Graphql in React, and do not know how to do it, you came to the right place.
Lazy loading images with Intersection Observer in React
Images can really be a bottleneck on the website. If you do not optimize your images users will have bad time and crawlers will not crawl you as fast as you want.
React: Pitch & anti-pitch
Weighing the Advantages and Disadvantages of React for Modern Web Development.