Implementing Infinite Scroll Pagination with React-Query v3
profile picture Aditya K
6 min read May 30, 2023

Implementing Infinite Scroll Pagination with React-Query v3

Introduction

Pagination is a common technique used to enhance performance and user experience when developing online apps that display vast volumes of data. A contemporary kind of pagination called infinite scroll, commonly referred to as endless scrolling, enables users to browse over content without having to click on pagination links. React-Query v3 is a powerful library that simplifies data-fetching and state management in React applications, making it an ideal choice for implementing infinite scroll pagination. In this blog post, we’ll look at how to build infinite scroll using React-Query and TypeScript in a React application to improve user experience and handle paginated data.

React-Query

React-Query is a powerful data-fetching and state management library for React. One of its notable features is the useInfiniteQuery hook, which simplifies the process of implementing infinite scroll pagination in React applications. In this tutorial, we will focus on using useInfiniteQuery to fetch and paginate data from an API in our React app.

Intersection Observer

IntersectionObserver is a web API that allows efficient detection of when an element is visible in the viewport or intersects with another element. We will be using IntersectionObserver for implementing pagination in our application. We can create a ref for the last element in the list, and then use IntersectionObserver to observe this element. When the last element becomes visible in the viewport, we can fetch more data to implement infinite scroll pagination. This allows us to efficiently load more data as the user scrolls, providing a smooth and optimized pagination experience.

Using Infinite Scroll with React-Query v3

Let us start by creating a react app.

yarn create vite infinite-scroll-app --template react-ts

To use infinite scroll pagination with React-Query v3, you’ll need to install the library and set up your query. First, install React-Query v3 and axios, a popular library for making HTTP requests, using npm or yarn:

yarn add react-query axios

Once you have installed React-Query v3 and axios, the next step is to set up your query and implement the infinite scroll logic. Before diving into that, let’s clean up the App.tsx file by removing all unnecessary code.

//App.tsx
import './App.css';

function App() {
  return <div>Infinate Scroll App</div>;
}

export default App;

Then, we need to set up a QueryClientProvider at the top level of our application to provide the QueryClient instance to all the child components:

//main.tsx
import React from 'react';
import 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') as HTMLElement).render(
  <QueryClientProvider client={queryClient}>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </QueryClientProvider>
);

We will now create a function to fetch data. To simulate pagination, we will use the PokeAPI as our endpoint.

//utils/fetchData.ts
import axios from 'axios';
import { QueryFunction } from 'react-query';

const LIMIT = 10

export interface ItemDataI {
  name: string;
}

interface APIResultsI {
  results: ItemDataI[];
  offset: number | null;
}

const fetchData: QueryFunction<APIResultsI, 'pokemon'> = async ({
  pageParam,
}) => {
  const offset = pageParam ? pageParam : 0;
  const data = await axios.get(
    `https://pokeapi.co/api/v2/pokemon?offset=${offset}&limit=${LIMIT}`
  );
  return {
    results: data.data.results,
    offset: offset + LIMIT,
  };
};

export default fetchData;

Next, let’s implement infinite scrolling. Start by implementing React-Query’s useInfiniteQuery hook.

//App.tsx
const {
    data,
    error,
    fetchNextPage,
    hasNextPage,
    isFetching,
    isLoading,
  } = useInfiniteQuery('data', fetchData, {
    getNextPageParam: (lastPage, pages) => lastPage.offset,
  });

The useInfiniteQuery hook returns an object containing the following properties:

  • data: the data returned by our fetchData function
  • error: any errors returned by fetchData function
  • fetchNextPage: a function that fetches next set of data
  • hasNextPage: a boolean indicating whether there is more data to fetch
  • isFetching: a boolean indicating whether data is currently being fetched
  • isLoading: a boolean indicating whether the query is currently loading. This will be true for only the initial load.

Additionally, the useInfiniteQuery hook includes a getNextPageParam function. This function determines the value of the pageParam parameter for the next page of data to fetch.

When using useInfiniteQuery from React-Query to fetch data, the returned data is usually an array of pages, where each page contains an array of items. To easily map through and render these items, we will flatten the data into a single array. We can use the useMemo hook to efficiently flatten the data and prevent unnecessary recalculations.

//App.tsx
const flattenedData = useMemo(
    () => (data ? data?.pages.flatMap(item => item.results) : []),
    [data]
);

To implement infinite scroll pagination, we need to create a reference for the last element in the list and set up an IntersectionObserver to detect when that element becomes visible on the screen. Once the last element is visible, we can trigger the fetchNextPage function to fetch more data.

//App.tsx
const observer = useRef<IntersectionObserver>();
  const lastElementRef = useCallback(
    (node: HTMLDivElement) => {
      if (isLoading) return;
      if (observer.current) observer.current.disconnect();
      observer.current = new IntersectionObserver(entries => {
        if (entries[0].isIntersecting && hasNextPage && !isFetching) {
          fetchNextPage();
        }
      });
      if (node) observer.current.observe(node);
    },
    [isLoading, hasNextPage]
  );

Finally, we render the list of items.

//App.tsx
if (isLoading) return <h1>Loading Data</h1>;

if (error) return <h1>Couldn't fetch data</h1>;

return (
    <div>
      <div>
        {flattenedData.map((item, i) => (
          <div
            key={i}
            ref={flattenedData.length === i + 1 ? lastElementRef : null}>
            <p>{item.name}</p>
          </div>
        ))}
      </div>
      {isFetching && <div>Fetching more data</div>}
    </div>
  );

We can use the isLoading and error returned by useInfiniteQuery to display loading and error messages, respectively.

To render the list, we simply map through the flattened data and return each item. We also attach lastElementRef to the last element in the list. This allows our IntersectionObserver to observe the last element and trigger fetchNextPage when it becomes visible on the screen.

And with that, we’ve successfully implemented a smooth infinite scroll using React-Query v3 and IntersectionObserver, providing a seamless pagination experience for your users.

Demo GIF

You can find the GitHub repository for the entire code here.

StackBlitz

Conclusion

In summary, implementing infinite scroll pagination with React-Query v3 and IntersectionObserver is a simple yet powerful feature that enhances the performance and user experience of your React applications. By leveraging the capabilities of React-Query for data fetching and state management, and utilizing the built-in IntersectionObserver API for detecting when to fetch more data, you can create a smooth and efficient pagination implementation without the need for additional external libraries or complex logic. This approach results in a seamless and responsive browsing experience for your users, making it a valuable addition to your web applications.

  1. Infinite scrolling
  2. React-Query v3 - useInfinateQuery
  3. Intersection Observer
  4. Axios
service category

Explore limitless possibilities with AntStack's frontend development capabilities. Empowering your business to achieve your most audacious goals. Build better.

Build with us

Author(s)

Tags

Share this blog

Your Digital Journey deserves a great story.

Build one with us.

Recommended Blogs

cookie

These cookies are used to collect information about how you interact with this website and allow us to remember you. We use this information in order to improve and customize your browsing experience and for analytics and metrics about our visitors on this website.

If you decline, your information won’t be tracked when you visit this website. A single cookie will be used in your browser to remember your preference not to be tracked.

Build With Us