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
}
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
},
})
}
Here, we can replace unused parameters with underscores like the following:
getNextPageParam: (lastPage, _, lastPageParam) => {
// -------------------------------------------------
getPreviousPageParam: (_, __, firstPageParam) => {
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
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)
}
},
})
}
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.
Top comments (0)