Better Programming

Advice for programmers.

Follow publication

How To Create a Custom useInfiniteScroll() With React Hooks

Oyetoke Tobiloba Emmanuel
Better Programming
Published in
5 min readDec 3, 2019

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:

  1. Added a useState to store and set our data.
  2. Added a useEffect to only load our data when the component is mounted.
  3. Check if data is ready or not.
  4. 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:

  1. We added a useState called page — will be incremented and used as pagination for our article API.
  2. We created two functions: loadData and moreData.
  3. loadData loads data the first time you load the page and is triggered once.
  4. moreData gets more data and is triggered once a user scrolls down to the bottom.
  5. In moreData, we increment the page state value and the set isFetching to false 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:

  1. Added a parameter callback that will be triggered whenever the user scrolls down.
  2. Added isFetching useState to signal when the data is loading or not.
  3. Added a useEffect lifecycle to call our callback when isFetching state changes and value equals true.
  4. Then, we export both isFetching and setIsFetching 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:

  1. Added a new useEffect lifecycle — It binds the isScrolling function to the scroll event.
  2. 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:

  1. We imported the newly created useInfinite Hook.
  2. We initialized the useInfiniteScroll custom Hook and passed it a function to trigger when the user scrolls down.
  3. We now have only one useEffect lifecycle that loads data on load once.
  4. 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/

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Write a response