TanStack Query for Data Fetching¶
TanStack Query (formerly React Query) is a powerful library for managing data fetching, caching, and synchronization in React applications. It provides a declarative API for handling asynchronous data operations, enabling developers to build scalable, performant, and maintainable enterprise applications. By abstracting away boilerplate logic for caching, retries, and background refetching, TanStack Query reduces the cognitive load of managing state in complex React apps.
Key Concepts and Architecture¶
TanStack Query operates on a query client that manages a global cache of data, along with mechanisms for fetching, updating, and invalidating data. Key components include:
1. Queries¶
- Fetch data from APIs using
useQuery. - Automatically handles caching, retries, and background refetching.
- Uses query keys to uniquely identify data requests (e.g.,
['users', { id: 1 }]).
2. Mutations¶
- Perform data mutations (e.g., create, update, delete) with
useMutation. - Supports optimistic updates, rollback on failure, and loading states.
3. Caching Strategy¶
- Data is cached in-memory and invalidated based on dependencies or time-to-live (TTL) settings.
- Supports partial updates and background refetching for stale data.
4. Query Client¶
- Centralized instance for managing the cache and shared state across the app.
- Used for manual invalidation, refetching, or global state management.
Diagram: A high-level architecture showing the query client, query hooks, and cache interaction.
Implementation Examples¶
1. Basic Data Fetching¶
import { useQuery } from '@tanstack/react-query';
function UserList() {
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: async () => {
const res = await fetch('/api/users');
return res.json();
},
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
2. Mutations with Optimistic Updates¶
import { useMutation, useQueryClient } from '@tanstack/react-query';
function UpdateUser({ userId, newName }) {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: async () => {
await fetch(`/api/users/${userId}`, {
method: 'PATCH',
body: JSON.stringify({ name: newName }),
});
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
return (
<button onClick={() => mutation.mutate()}>
Update Name
</button>
);
}
3. Custom Query Hook for Reusability¶
import { useQuery } from '@tanstack/react-query';
function useFetchUser(id: number) {
return useQuery({
queryKey: ['users', id],
queryFn: async () => {
const res = await fetch(`/api/users/${id}`);
return res.json();
},
});
}
Best Practices¶
- Use Descriptive Query Keys
- Avoid dynamic keys with user input (e.g.,
['user', id]instead of['user', '123']). -
Use
queryKeyarrays to ensure uniqueness and avoid cache collisions. -
Leverage Background Refetching
-
Set
refetchIntervalfor real-time data (e.g.,refetchInterval: 5000). -
Handle Errors Gracefully
-
Use
useQuery'serrorstate to display user-friendly error messages. -
Optimize Caching
- Use
staleTimeto delay refetching until data is stale. - Combine related queries into a single request using
queryFnfor efficiency.
Integration with Next.js¶
In Next.js, TanStack Query works seamlessly with both client and server components:
- Server Components: Use useQuery in server components to fetch data, but ensure it's only used in client-rendered sections.
- API Routes: Use the query client to invalidate cached data after server-side mutations.
Example:
// pages/api/updateUser.ts
export default async function handler(req, res) {
const { id, name } = req.body;
await fetch(`https://api.example.com/users/${id}`, {
method: 'PATCH',
body: JSON.stringify({ name }),
});
res.status(200).json({ success: true });
}
Advanced Topics¶
- Pagination with Infinite Queries
-
Use
useInfiniteQueryto load data in chunks (e.g., for large datasets). -
Real-Time Updates
-
Combine TanStack Query with WebSockets or GraphQL subscriptions for live data.
-
Global State Management
- Use the query client to share state across components (e.g., authentication tokens or user preferences).
Key Takeaways¶
- TanStack Query simplifies data fetching, caching, and synchronization in React apps.
- Use
useQueryfor fetching data anduseMutationfor mutations with optimistic updates. - Prioritize query key design and caching strategies to avoid redundant requests.
- Integrate with Next.js for server-side rendering and API route synchronization.
- Leverage advanced features like infinite queries and real-time updates for complex use cases.