Parallel queries when working with 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

This is the most straightforward when you need independent data in the same component:
function Dashboard() {
const { data: repos, isLoading: reposLoading } = useQuery({
queryKey: ['repos'],
queryFn: fetchRepos
})
const { data: members, isLoading: membersLoading } = useQuery({
queryKey: ['members'],
queryFn: fetchMembers
})
// Each loads independently - great UX!
if (reposLoading) return <div>Loading repos...</div>
if (membersLoading) return <div>Loading members...</div>
return (
<div>
<ReposList repos={repos} />
<MembersList members={members} />
</div>
)
}
This is where it becomes really useful when you need to get data for an unknown number of items:
function useRepoIssues(repos) {
return useQueries({
queries: repos?.map(repo => ({
queryKey: ['repos', repo.name, 'issues'],
queryFn: () => fetchIssues(repo.name)
})) ?? []
})
}
function ReposDashboard() {
const { data: repos } = useQuery({
queryKey: ['repos'],
queryFn: fetchRepos
})
const issuesQueries = useRepoIssues(repos)
// Derive total issues across all repos
const totalIssues = issuesQueries
.map(query => query.data?.length ?? 0)
.reduce((sum, count) => sum + count, 0)
return (
<div>
<h2>Total Issues: {totalIssues}</h2>
{repos?.map((repo, index) => (
<RepoCard
key={repo.id}
repo={repo}
issues={issuesQueries[index]?.data}
isLoading={issuesQueries[index]?.isLoading}
/>
))}
</div>
)
}
// queries.js
export const repoOptions = {
queryKey: ['repos'],
queryFn: fetchRepos,
staleTime: 5 * 60 * 1000 // 5 minutes
}
export const membersOptions = {
queryKey: ['members'],
queryFn: fetchMembers,
staleTime: 10 * 60 * 1000 // 10 minutes
}
// hooks.js
export const useRepos = () => useQuery(repoOptions)
export const useMembers = () => useQuery(membersOptions)
export const useReposAndMembers = () => useQueries({
queries: [repoOptions, membersOptions]
})
function UserProfile({ userId }) {
const queries = useQueries({
queries: [
{
queryKey: ['user', userId],
queryFn: () => fetchUser(userId)
},
{
queryKey: ['user', userId, 'posts'],
queryFn: () => fetchUserPosts(userId)
},
{
queryKey: ['user', userId, 'followers'],
queryFn: () => fetchUserFollowers(userId)
}
]
})
const [user, posts, followers] = queries
const isLoading = queries.some(q => q.isLoading)
const hasError = queries.some(q => q.isError)
if (isLoading) return <ProfileSkeleton />
if (hasError) return <ErrorMessage />
return (
<div>
<UserInfo user={user.data} />
<PostsList posts={posts.data} />
<FollowersList followers={followers.data} />
</div>
)
}
The main point is that parallel queries offer the best of both worlds. They allow for separate caching and management while also providing coordinated loading states when needed. This is much better than using Promise.all, which ties everything together!