Infinite Queries in React Query

Just a guy who loves to write code and watch anime.
Search for a command to run...

Just a guy who loves to write code and watch anime.
No comments yet. Be the first to comment.
Qualities that make outstanding builders.

Bipedal vs quadrupedal: how many legs This is about the number of legs the creature walks on. Bipedal. Two legs. Humans, ostriches, T-rex, kangaroos, most fantasy humanoids. Quadrupedal. Four legs. Do

Intro You hear "lerp" and "smoothstep" everywhere in game dev. They sound like math jargon. They're not. Both are small tools that do the same job: smoothly move from one value to another. The problem

What is Kinematics Kinematics is the math field. It's the study of how things move without worrying about forces (which would be dynamics). FK and IK are the two branches: forward kinematics and inver

Intro Textures are usually the biggest cost in a 3D scene. Memory, bandwidth, and load time all get eaten by them. Resizing them is the obvious lever. There's more. This post is about the less obvious

Instead of managing page state yourself, React Query handles it:
function usePosts() {
return useInfiniteQuery({
queryKey: ['posts'],
queryFn: ({ pageParam }) => fetchPosts(pageParam), // pageParam managed by RQ
initialPageParam: 1, // Where to start
getNextPageParam: (lastPage, allPages, lastPageParam) => {
if (lastPage.length === 0) {
return undefined // No more pages
}
return lastPageParam + 1
}
})
}
You get back a different data shape:
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = usePosts()
// data.pages = [
// [post1, post2, post3], // page 1
// [post4, post5, post6], // page 2
// [post7, post8, post9] // page 3
// ]
const allPosts = data?.pages.flat() // Flatten into single array
Trigger next page on scroll:
import { useIntersectionObserver } from "@uidotdev/usehooks"
function InfinitePostList() {
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = usePosts()
const [ref, entry] = useIntersectionObserver()
// Auto-fetch when scrolled to bottom
React.useEffect(() => {
if (entry?.isIntersecting && hasNextPage && !isFetchingNextPage) {
fetchNextPage()
}
}, [entry?.isIntersecting, hasNextPage, isFetchingNextPage, fetchNextPage])
return (
<div>
{data?.pages.flat().map(post => (
<PostCard key={post.id} post={post} />
))}
{/* Trigger element at bottom */}
<div ref={ref}>
{isFetchingNextPage ? 'Loading more...' : 'Scroll for more'}
</div>
</div>
)
}
For APIs that return cursors instead of page numbers:
function useProjects() {
return useInfiniteQuery({
queryKey: ['projects'],
queryFn: ({ pageParam = 0 }) => fetchProjects(pageParam),
initialPageParam: 0,
getNextPageParam: (lastPage) => {
return lastPage.nextCursor ?? undefined // Return cursor or undefined
}
})
}
For chat apps where you can scroll up and down:
useInfiniteQuery({
queryKey: ['messages', chatId],
queryFn: ({ pageParam }) => fetchMessages(chatId, pageParam),
initialPageParam: 50, // Start in middle
getNextPageParam: (lastPage, allPages, lastPageParam) => {
return lastPage.length ? lastPageParam + 1 : undefined
},
getPreviousPageParam: (firstPage, allPages, firstPageParam) => {
return firstPageParam > 1 ? firstPageParam - 1 : undefined
}
})
Prevent infinite memory growth if necessary:
useInfiniteQuery({
queryKey: ['posts'],
queryFn: ({ pageParam }) => fetchPosts(pageParam),
initialPageParam: 1,
getNextPageParam: (lastPage, allPages, lastPageParam) => {
return lastPage.length ? lastPageParam + 1 : undefined
},
maxPages: 3 // Only keep 3 pages in cache
})