DEV Community

Cover image for Infinite Scrolling of TanStack Query ๐ŸŒธ
Abdul Ahad Abeer
Abdul Ahad Abeer

Posted on โ€ข Originally published at abeer.hashnode.dev

Infinite Scrolling of TanStack Query ๐ŸŒธ

This article is a continuation of the previous article. So, check that out before you continue.

For infinite scrolling, create an axios function for api calling:

export const getProducts = async ({ pageParam }: { pageParam: number }) => {
  return (
    await axiosInstance.get<Product[]>(
      `products?_page=${pageParam + 1}&_limit=3`
    )
  ).data
}
Enter fullscreen mode Exit fullscreen mode

Now, we need to define an infinite-scrolling specific query:

export function useProducts() {
  return useInfiniteQuery({
    queryKey: ["products"],
    queryFn: getProducts,
    initialPageParam: 0,
    getNextPageParam: (lastPage, allPages, lastPageParam) => {
      if (lastPage.length === 0) {
        return undefined
      }
      return lastPageParam + 1
    },
    getPreviousPageParam: (firstPage, allPages, firstPageParam) => {
      if (firstPageParam <= 1) {
        return undefined
      }
      return firstPageParam - 1
    },
  })
}
Enter fullscreen mode Exit fullscreen mode

Here, we can replace unused parameters with underscores like the following:

getNextPageParam: (lastPage, _, lastPageParam) => {

// -------------------------------------------------

getPreviousPageParam: (_, __, firstPageParam) => {
Enter fullscreen mode Exit fullscreen mode

The properties of useInfiniteQuery are pretty much self-explanatory. Now, implement the infinite query mechanisms in the react component: (This one is long. There will be some explanation after the code)

import { FC, Fragment, useState } from "react"
import { useProduct, useProducts } from "../services/queries"

interface ComponentProps {}

const Products: FC<ComponentProps> = () => {
  const [selectedProductId, setSelectedProductId] = useState<number | null>(
    null
  )

  const productsQuery = useProducts()
  const productQuery = useProduct(selectedProductId)

  return (
    <>
      {/* page data rendered here */}
      {productsQuery.data?.pages.map((page, index) => (
        <Fragment key={index}>
          {page.map((product) => (
            <Fragment key={product.id}>
              {/* click button to see in detail */}
              <button onClick={() => setSelectedProductId(product.id)}>
                {product.name}
              </button>
              <br />
            </Fragment>
          ))}
        </Fragment>
      ))}
      <br />

      {/* button to load to the next page */}
      <div>
        <button
          onClick={() => productsQuery.fetchNextPage()}
          disabled={
            !productsQuery.hasNextPage || productsQuery.isFetchingNextPage
          }
        >
          {productsQuery.isFetchingNextPage
            ? "Loading more..."
            : productsQuery.hasNextPage
            ? "Load More"
            : "Nothing more to load"}
        </button>
      </div>

      {/* selected product in detail */}
      <div>Selected product:</div>
      {JSON.stringify(productQuery.data)}
    </>
  )
}

export default Products
Enter fullscreen mode Exit fullscreen mode

useProducts is used for making pagination api call. So, we get data of the first page and render them initially.

Then, another mechanism is added to select a product and see data in detail. In the rendered page data, there is a button to select, and its data is shown in the bottom. The selected data comes from useProduct api calling. The following is how it works:

// api.ts
export const getProduct = async (id: number) => {
  return (await axiosInstance.get<Product>(`products/${id}`)).data
}

// queries.ts
export function useProduct(id: number | null) {
  const queryClient = useQueryClient()

  return useQuery({
    queryKey: ["product", { id }],
    queryFn: () => getProduct(id!),
    enabled: !!id,
    placeholderData: () => {
      const cachedProducts = (
        queryClient.getQueryData(["products"]) as {
          pages: Product[] | undefined
        }
      )?.pages?.flat(2)

      if (cachedProducts) {
        return cachedProducts.find((item) => item.id === id)
      }
    },
  })
}
Enter fullscreen mode Exit fullscreen mode

Here getProduct is for making the api call.

We call the api through useProduct. this api calling works only when there is an id passed through, as enabled is set to idโ€™s existence.

placeholderData sets cached data when data is being fetched and fills the place with cached data.

AWS Q Developer image

Your AI Code Assistant

Automate your code reviews. Catch bugs before your coworkers. Fix security issues in your code. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

Top comments (0)

The best way to debug slow web pages cover image

The best way to debug slow web pages

Tools like Page Speed Insights and Google Lighthouse are great for providing advice for front end performance issues. But what these tools canโ€™t do, is evaluate performance across your entire stack of distributed services and applications.

Watch video

๐Ÿ‘‹ Kindness is contagious

Please leave a โค๏ธ or a friendly comment on this post if you found it helpful!

Okay