Data Fetching in React with TanStack Query
Fetching in useEffect means handling loading, errors, caching, refetching and race conditions yourself. TanStack Query (formerly React Query) handles all of that for client-side data.
Setup
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Projects />
</QueryClientProvider>
);
}
Reading data with useQuery
import { useQuery } from '@tanstack/react-query';
function Projects() {
const { data, isPending, error } = useQuery({
queryKey: ['projects'],
queryFn: () => fetch('/api/projects').then((r) => r.json()),
staleTime: 60_000, // treat as fresh for a minute
});
if (isPending) return <p>Loading...</p>;
if (error) return <p>Something went wrong.</p>;
return data.map((p) => <ProjectCard key={p.id} project={p} />);
}
Query keys
The key identifies the data in the cache. Include every variable the request depends on:
useQuery({ queryKey: ['projects', { status, page }], queryFn: () => fetchProjects({ status, page }) });
Changing data with useMutation
import { useMutation, useQueryClient } from '@tanstack/react-query';
function NewProject() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (name) => fetch('/api/projects', { method: 'POST', body: JSON.stringify({ name }) }),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['projects'] }), // refetch lists
});
return (
<button disabled={mutation.isPending} onClick={() => mutation.mutate('Website redesign')}>
Create project
</button>
);
}
What you get for free
- Shared cache: two components using the same key make one request.
- Background refetch when the window regains focus or the network reconnects.
- Retries with backoff, pagination and infinite queries.
- Devtools to inspect the cache.
If you use a framework with Server Components, fetch on the server where you can and use TanStack Query for interactive, client-driven data.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.