How to Implement React Infinite Scrolling with React Query v5

Pieces 🌟 - Mar 1 - - Dev Community

How to Implement React Infinite Scrolling with React Query v5.

As developers, aiming for users to have the best experience when navigating your application is one of the top priorities. In addition to writing code and implementing complex functionalities, there should be a conscious effort to ensure maximum user experience and high performance of the application.

As a front-end developer, every bit of code, function or logic comes down to rendering content to users, so the appropriate measures should be taken in rendering content in specific use cases.

Consider a scenario where a huge chunk of data is to be displayed, rendering all that data to the Document Object Model (DOM) at once could have negative effects especially on the application's overall performance.

One way to get around this is to implement pagination, which entails creating pages and rendering a particular length of content per page. Another solution to this, one which we will discuss in this blog post, is to implement React infinite scrolling which allows you to render more content to users only when they reach the bottom of the page.

In this tutorial, you will learn how to implement React infinite scrolling using React Query v5. Let's dive in!

React Infinite Scrolling - What's that about?

Infinite scrolling is a content rendering technique that displays content as the user continuously scrolls to the bottom of the page. One key difference between infinite scrolling and pagination, another content rendering technique, is that infinite scrolling does not require the user to click a button to load more content. It is simply dependent on the user's action of scrolling down. Interesting, isn't it?

React Query

React Query is a JavaScript state management library used for data fetching and caching in React applications. React Query offers the useInfiniteQuery hook which abstracts the complexities of implementing infinite scrolling in applications.

Prerequisites

Just before we get into the technical aspect and implementation of logic, here are a few things that would be great to know to effectively follow through this tutorial:

Getting Started

We're going to dive right into the logic to implement React infinite scrolling but just before we do that, let's get a few things set up in our environment.

Run this command in your terminal to add install React in your environment:

npm create vite infinite-scroll-app react
Enter fullscreen mode Exit fullscreen mode

The next step will be to install the react-query library which we will be using to implement the logic. Still on your terminal, run this command:

npm install react-query
Enter fullscreen mode Exit fullscreen mode

We're quite set on the installation bit. Now let's make some tweaks to our main.jsx file(this may be your index.jsx file if you're using another build tool). Here's what the main.jsx file looks like:

import * as React from "react";
import * as ReactDOM from "react-dom/client";
import { QueryClient, QueryClientProvider } from "react-query";
import App from "./App";
import "./index.css";

const queryClient = new QueryClient();

ReactDOM.createRoot(document.getElementById("root")!).render(
  <QueryClientProvider client={queryClient}>
    <App />
  </QueryClientProvider>);
Enter fullscreen mode Exit fullscreen mode

Here, we imported the QueryClientProvider component and the QueryClient instance which basically allows you to use the built-in hooks in the react-query library by wrapping your App component in it.

Fetching Data

Earlier, we discussed React query being a library for data fetching, that's the logic behind this - it controls how data is being served. To implement React infinite scrolling, we definitely have to scroll through something, some content or data of some sort. This implementation is incomplete without the presence of data. Now let's get that data! I'll be making use of the Poke API which gives some mock data for our usage.

const dataList = async ({
  page,
}) => {
  const offset = page ? page : 0;
  const res = await fetch(
    `https://pokeapi.co/api/v2/pokemon?offset=${offset}&limit=10`
  );

  const data = await res.json();
  console.log("data = ", data);
  return {
    results: data.results,
    offset: offset + 10,
  };
};

export default dataList;
Enter fullscreen mode Exit fullscreen mode

Here, we're fetching our data from the API and then we set the limit to 10. The expected behavior for this is to render 10 values on the screen when the page is initially clicked upon and then continuously add more values in tens since the offset is set to offset + 10, you can toggle this value if you want to add more values when the user reaches the bottom screen.

Now that we have our data fetched from the API, we can proceed to implement React infinite scrolling.

Implement Scrolling

In this section, we will get into the logic of implementing React infinite scrolling.

The UseInfiniteQuery hook

Here, we will use the useInfiniteQuery hook from the React query library. Consider the block of code below:

const {
    data,
    error,
    fetchNextPage,
    hasNextPage,
    isFetching,
    isLoading,
  } = useInfiniteQuery('data', dataList, {
    getNextPageParam: (lastPage, pages) => lastPage.offset,
  });
Enter fullscreen mode Exit fullscreen mode

The useInfiniteQuery hook is the foundation of this implementation as it provides properties around the:

  • data, which contains the data from the API.
  • error, which handles any error from fetching the data from the API.
  • fetchNextpage, which fetches the next set of data to be displayed when the user reaches the bottom.
  • hasNextpage, which checks to see if there's any more data to be displayed.
  • isFetching, which shows when the data is currently being fetched.
  • isLoading, which shows when the first set of data is being loaded from the API.

Detecting an Element's Position with IntersectionObserver

You might have been wondering how the DOM knows when the user scrolls to the bottom of the page. Well, that's where the IntersectionObserver API comes in! The IntersectionObserver API provides a way to detect an element's position relative to the root element or viewport. Consider the block of code below:

const handleObserver = useRef<IntersectionObserver>();
  const lastElement = useCallback(
    (element) => {
      if (isLoading) return;
      if (observer.current) handleObserver.current.disconnect();
      handleObserver.current = new IntersectionObserver(entries => {
        if (entries[0].isIntersecting && hasNextPage && !isFetching) {
          fetchNextPage();
        }
      });
      if (element) handleObserver.current.observe(element);
    },
    [isLoading, hasNextPage]
  );
Enter fullscreen mode Exit fullscreen mode

This piece of code observes the user's motion on the screen. If the user reaches the bottom of the screen, the IntersectionObserver is alerted and serves the next set of data. Technically, it takes note of the point where the last element in the data intersects and then if hasNextPage is true, it fetches the new set of data to display.

Finishing up

The final bit of action is to render the data and logic to the DOM but just before we do that. Let's flatten the data from the API into a single array so it is easier to map through and display on the DOM. We can use React's useMemo() hook for this:

const flatData = useMemo
    () => (data ? data?.pages.flatMap(item => item.result) : []),
    [data]
);
Enter fullscreen mode Exit fullscreen mode

And then:

if (isLoading) return <h1>Just a second...</h1>; 
  if (error) return <h1>Oops! Something went wrong</h1>;

  return (
    <div>
      <div>
        {flatData.map((item, i) => (
          <div
            className="flex flex-col justify-center items-center [&>*]:my-5"
            key={i}
            ref={flatData.length === i + 1 ? lastElement : null}
          >
            <h1 className="text-5xl">{item.name}</h1>
          </div>
        ))}
      </div>
      {isFetching && <div>Hold on...</div>}
    </div>
Enter fullscreen mode Exit fullscreen mode

And here we are. Added some error handling in there and Tailwind CSS styling to make the text look bigger.

Displaying Result

Here's what it looks like:

Scrolling through the results.

If you look at the scroll bar, you see that when it approaches the bottom which seems like the end, it moves up, a reaction to new data being loaded to the DOM.

Conclusion

In this tutorial, you have learnt about React query v5 and how to implement React infinite scrolling using React query v5. Feel free to make more tweaks and explore.

Stay up to date on React 18 with our comprehensive guide. Until then, Happy Coding!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .