React Query
React Query (TanStack Query) is a server state manager - it handles the full lifecycle of asynchronous data: fetching, caching, synchronizing, and updating.
The problem it solves
Without React Query, server data management requires lots of manual work:
tsx
// manual approach - what you have to write yourself
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetch('/api/users')
.then(r => r.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, []);
// missing: caching, deduplication, background refetch, stale data...With React Query
tsx
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/users').then(r => r.json()),
staleTime: 5 * 60 * 1000, // data is fresh for 5 minutes
});What it handles automatically
- Caching and deduplication of identical requests.
- Background refetching when window is refocused.
- Automatic retries on failure.
- Pagination and infinite scroll.
- Optimistic updates.
- Cache invalidation.
Mutations
tsx
const mutation = useMutation({
mutationFn: (newUser) => fetch('/api/users', {
method: 'POST',
body: JSON.stringify(newUser),
}),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] }); // refetch list
},
});
mutation.mutate({ name: 'Alice', email: 'alice@example.com' });Why it's not a traditional state manager
| Redux / Zustand | React Query | |
|---|---|---|
| State type | Client state | Server state |
| Source of truth | The store | The server |
| Writes | Direct | Mutations + invalidation |
| Caching | Manual | Built-in |
React Query doesn't replace Zustand or Redux - it replaces the API data slices you used to put inside them. Use both: React Query for server data, Zustand for client state.
updated
did you know it?