How To Create a Custom useInfiniteScroll() With React Hooks
Infinite scrolling should be easier

In one of my articles, I explained how to create custom useModal()
React Hooks that can be hooked into modal components.
In this article, I will explain how to create useScrollInfinite()
React Hooks that can be used with an infinite scrolling component. An infinite scroll is a modern approach to pagination, you don’t have to go to the next page to see more content.
It automatically detects if you are at the end of the page and loads more content using Ajax and other asynchronous data fetching techniques.
Infinite scroll gives a great performance boost to modern applications because you only display a bit of data on page load, and then fetch more data as they scroll down the page, which makes the application fast.
Infinite scroll usually works excellently for handling lists of things like text, images, videos, status updates, feeds, and table data.
In this tutorial, I will be using a REST API called Medrum to get random articles. It scrapes articles from all over the web and crunches it to pieces of data.
Build a List (Article) Component
The first thing to do is to build a simple list component. You can set up a React app using Create React App.
In this tutorial, I will be using axios as HTTP client library. You can install it as below:
yarn add axios
To further know how to send HTTP request in React, watch the video below:
Now, let’s look at the below simple component:
Here’s what we did above:
- Added a
useState
to store and set our data. - Added a
useEffect
to only load our data when the component is mounted. - Check if data is ready or not.
- Then, map through the list of articles and display them in
<li>
.
Here’s what it looks like:

If you scroll down, it doesn’t load more data, since it can’t detect any bottom yet and that’s our next phase of the tutorial. Let’s see how we can pull that off.
Loading More Data by Detecting the Bottom
Now, let’s see how we can load more data by detecting when you have scrolled to the bottom of the body or a specific container.
There are many ways to do this, it depends on your use case and what your DOM looks like.
However, the most common way is to check if the Window window
object’s inner height, plus the document window
object’s scrollTop
is equal to the document’s offsetHeight
.
window.addEventListener("scroll", ()=>{
if(window.innerHeight + document.documentElement.scrollTop!==document.documentElement.offsetHeight){
return;
}
else {
console.log("scrolling down");
}
})
With the above code, we would be able to know when a user scrolled to the bottom of the page.
Then, it can be used in our useEffect
lifecycle Hook like below:
function isScrolling(){
if(window.innerHeight + document.documentElement.scrollTop!==document.documentElement.offsetHeight){
return;
}
else {
console.log("scrolling down");
}}useEffect(()=>{
window.addEventListener("scroll", isScrolling);
return () => window.removeEventListener("scroll", isScrolling);}, [])
In the above code, we registered our isScrolling
function to listen to the event scroll
. So now, whenever a user scrolls, the isScrolling
is called.
Since we’ll be fetching data when a user scrolls down, we need to know when data is done fetching, hence we have the below:
const [isFetching, setIsFetching] = useState(false);function isScrolling(){
if(window.innerHeight + document.documentElement.scrollTop!==document.documentElement.offsetHeight){
return;
}
else {
setIsFetching(true)
}}useEffect(() => {
if (isFetching){
console.log("Fetch data")
}
}, [isFetching]);
Now we need to fetch data from the article API I mentioned earlier and we’ll be using axios
:
const [page, setPage] = useState(1);const fetchData = () =>{
let url = "https://medrum.herokuapp.com/articles";
axios.get(url).then(res => {
setData(res.data);
});
}const moreData = () => {
let url = `https://medrum.herokuapp.com/feeds/?source=5718e53e7a84fb1901e05971&page=${page}&sort=popular`;
axios.get(url).then(res => {
setData([...data, ...res.data]);
setPage(page+1)
setIsFetching(false)
});
}
What we did above:
- We added a
useState
calledpage
— will be incremented and used as pagination for our article API. - We created two functions:
loadData
andmoreData
. loadData
loads data the first time you load the page and is triggered once.moreData
gets more data and is triggered once a user scrolls down to the bottom.- In
moreData
, we increment the page state value and the setisFetching
tofalse
to signal that the data has finished loading.
Then, putting all this together, we can write our List
component as below:
We added two useEffect
— the first one to load our data when the browser loads and bind our isScrolling
callback to the event listener as well, while the second one listens to the isFetching
useState
, then gets more data when it's true
.
Building a Custom React Hook
We’ve seen how to load more data when a user scrolls down to the bottom of the page and everything seems to be working fine.
However, we have only implemented it in our article component, not a custom React Hook that can be reused by another component.
Let’s look at how to implement this in a custom React Hook.
Create a new useInfinite.js
file in your src
directory and let's add a custom React Hook function like below:
const useInfiniteScroll = callback => {
const [isFetching, setIsFetching] = useState(false);
useEffect(() => {
if (!isFetching) return;
callback();
}, [isFetching]);
return [isFetching, setIsFetching];
};
export default useInfiniteScroll;
In our useInfiniteScroll
custom Hook, we:
- Added a parameter
callback
that will be triggered whenever the user scrolls down. - Added
isFetching
useState
to signal when the data is loading or not. - Added a
useEffect
lifecycle to call our callback whenisFetching
state changes and value equalstrue
. - Then, we export both
isFetching
andsetIsFetching
so it can be accessible in functional components.
The next thing now is to register our isScrolling
function to the scroll
event:
What we did above:
- Added a new
useEffect
lifecycle — It binds theisScrolling
function to the scroll event. - We added our
isScrolling
function.
That’s all, our custom React Hook is ready. Let’s see how it can be implemented with our article component. Head over to the article component and refactor as below:
In the above, we made some changes:
- We imported the newly created
useInfinite
Hook. - We initialized the
useInfiniteScroll
custom Hook and passed it a function to trigger when the user scrolls down. - We now have only one
useEffect
lifecycle that loads data on load once. - We refactored our
moreData
function from function expression to function declaration.
That’s all. Finally, we can now use our custom React Hook with any component to load data when the user scrolls down.
You can find the whole code in the GitHub repo:
To see a live demo, you can check it out here: https://react-useinfinitescroll.now.sh/