Enable Dark Mode!
how-to-build-infinite-scroll-in-react-with-tanstack-query.jpg
By: Alan Joy

How to Build Infinite Scroll in React with TanStack Query

Technical odoo React

Infinite scrolling has become a common feature in most modern web applications. We can see that instead of showing a pagination and next page button, the new content is automatically loading as we scroll down through the content. We can see this behavior on most of the social media applications, e-commerce websites, and other content platforms. This has become a more engaging and user-friendly technique.

In a React application, implementing this infinite scroll feature and manually handling the pagination, loading states, and data fetching can be challenging and hard to maintain. So TanStack Query is the perfect solution for this. In this blog, we can learn and build an infinite scrolling list using React and TanStack Query.

What is Infinite Scroll?

Infinite scroll is a UI pattern where the next page data is automatically loaded when the user reaches the bottom of the page or the container. There are no additional UI clicks for loading new data. This creates a smooth user experience and reduces unnecessary user interactions.

Initial Setup

First, set up a React application if you don't have one.

npm create vite@latest my-app
cd my-app
npm install

Install the tanstack query package

npm install @tanstack/react-query

Now, we need to wrap our application with a query client so we can use TanStack Query hooks.

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Users } from "./Users";
const queryClient = new QueryClient();
function App() {
 return (
   <QueryClientProvider client={queryClient}>
     <Users />
   </QueryClientProvider>
 );
}
export default App;

Creating the Fetch function

For this example, we can use this API for users' data.

https://randomuser.me/api/?page=1&results=10

Now, we can create a function to fetch the users.

const fetchUsers = async ({ pageParam = 1 }) => {
 const response = await fetch(
   `https://randomuser.me/api/?page=${pageParam}&results=10`,
 );
 return response.json();
};

Using useInfiniteQuery

Now, we can configure an infinite query using the useInfiniteQuery hook of TanStack Query.

import { useInfiniteQuery } from "@tanstack/react-query";
const {
 data,
 fetchNextPage,
 hasNextPage,
 isFetchingNextPage,
 isPending,
 isError,
} = useInfiniteQuery({
 queryKey: ["users"],
 queryFn: fetchUsers,
 initialPageParam: 1,
 getNextPageParam: (lastPage, allPages) => {
   return allPages.length + 1;
 },
});
  • queryKey - this key is used by TanStack Query for caching
  • queryFn - this function is called to fetch data from
  • initialPageParam - sets the initial page value as this
  • getNextPageParam - use this function to determine which page should be loaded next

Rendering the Data

The data that is returned by useInfiniteQuery contains multiple pages. So, we need to loop through all pages and render the user details.

if (isPending) {
 return <p>Loading...</p>;
}
return (
 <div>
   {data.pages.map((page, pageIndex) => (
     <div key={pageIndex}>
       {page.results.map((user) => (
         <div key={user.login.uuid}>
           <h3>
             {user.name.first} {user.name.last}
           </h3>
         </div>
       ))}
     </div>
   ))}
 </div>
);

Detecting When Users Reach the Bottom

Now, we can set up a method to detect if the user reaches the bottom to trigger the fetching of the next page.

import { useRef, useEffect } from "react";
const loadMoreRef = useRef(null);
useEffect(() => {
 const observer = new IntersectionObserver(
   (entries) => {
     if (
       entries[0].isIntersecting &&
       hasNextPage &&
       !isFetchingNextPage
     ) {
       fetchNextPage();
     }
   },
   {
     threshold: 1,
   }
 );
 if (loadMoreRef.current) {
   observer.observe(loadMoreRef.current);
 }
 return () => observer.disconnect();
}, [fetchNextPage, hasNextPage, isFetchingNextPage]);

Adding the Trigger Element

<div ref={loadMoreRef}>
 {isFetchingNextPage ? "Loading more..." : ""}
</div>;

Now, when a user scrolls to this element, the TanStack Query can fetch the next page.

Handling Errors

Add this code to handle API errors gracefully.

if (isError) {
 return <p>Something went wrong.</p>;
}

Full code

import { useInfiniteQuery } from "@tanstack/react-query";
import { useEffect, useRef } from "react";
const fetchUsers = async ({ pageParam = 1 }) => {
 const response = await fetch(
   `https://randomuser.me/api/?page=${pageParam}&results=10`,
 );
 return response.json();
};
export function Users() {
 const {
   data,
   fetchNextPage,
   hasNextPage,
   isFetchingNextPage,
   isPending,
   isError,
 } = useInfiniteQuery({
   queryKey: ["users"],
   queryFn: fetchUsers,
   initialPageParam: 1,
   getNextPageParam: (lastPage, allPages) => {
     return allPages.length + 1;
   },
 });
 const loadMoreRef = useRef(null);
 useEffect(() => {
   const observer = new IntersectionObserver(
     (entries) => {
       if (entries[0].isIntersecting && hasNextPage && !isFetchingNextPage) {
         fetchNextPage();
       }
     },
     {
       threshold: 1,
     },
   );
   if (loadMoreRef.current) {
     observer.observe(loadMoreRef.current);
   }
   return () => observer.disconnect();
 }, [fetchNextPage, hasNextPage, isFetchingNextPage]);
 if (isPending) {
   return <div>Loading...</div>;
 }
 if (isError) {
   return <div>Something went wrong.</div>;
 }
 return (
   <div>
     {data.pages.map((page, pageIndex) => (
       <div key={pageIndex}>
         {page.results.map((user) => (
           <div key={user.login.uuid}>
             <h3>
               {user.name.first} {user.name.last}
             </h3>
           </div>
         ))}
       </div>
     ))}
     <div ref={loadMoreRef}>
       {isFetchingNextPage ? "Loading more..." : ""}
     </div>
   </div>
 );
}

Infinite scrolling can improve the user experience while browsing large datasets. However, implementing it manually can become hard to maintain. TanStack Query can help in this case and simplify the entire process with useInfiniteQuery. We can combine this with the Intersection Observer API to build a smooth and performant infinite scrolling experience.

To read more about How to Implement Optimistic Updates in React with TanStack Query, refer to our blog How to Implement Optimistic Updates in React with TanStack Query.


Frequently Asked Questions

When should I use useInfiniteQuery?

Use useInfiniteQuery when you need infinite scrolling or a "Load More" feature in your application.

Why use an Intersection Observer for infinite scroll?

It efficiently detects when users reach the bottom of the page without relying on scroll event listeners.

Does TanStack Query cache infinite scroll data?

Yes, TanStack Query automatically caches fetched pages to improve performance and reduce unnecessary API requests.

If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



WhatsApp