;
}
```
**Comparison:**
- `useSWR(key, fetcher)` → `useQuery({ queryKey: [key], queryFn: fetcher })`
- `mutate()` → `queryClient.invalidateQueries()`
- `!data` loading → `isLoading`
- `useSWRConfig()` → `useQueryClient()`
---
## DevTools
**Setup DevTools:**
```tsx
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
function App() {
return (
);
}
```
**Production Build:**
```tsx
// DevTools are automatically excluded in production builds
// No need to conditionally render
```
**DevTools Features:**
- View all queries and their states
- Inspect query data and errors
- Manually trigger refetch
- Invalidate queries
- View query timelines
- Monitor cache size
- Debug network waterfalls
---
## Common Pitfalls
**❌ Don't Create QueryClient Inside Component:**
```tsx
// WRONG - Creates new client on every render
function App() {
const queryClient = new QueryClient(); // ❌
return ...;
}
// CORRECT - Stable client instance
function App() {
const [queryClient] = useState(() => new QueryClient()); // ✅
return ...;
}
```
**❌ Don't Use Query Data in Render Without Checking:**
```tsx
// WRONG - data might be undefined
function UserProfile() {
const { data } = useQuery({ queryKey: ['user'], queryFn: fetchUser });
return
{data.name}
; // ❌ Crashes if data is undefined
}
// CORRECT - Handle loading state
function UserProfile() {
const { data, isLoading } = useQuery({ queryKey: ['user'], queryFn: fetchUser });
if (isLoading) return ; // ✅
return
{data.name}
;
}
```
**❌ Don't Forget Query Keys Are Dependencies:**
```tsx
// WRONG - Missing dependency in query key
function UserPosts({ userId, filter }: Props) {
const { data } = useQuery({
queryKey: ['posts'], // ❌ Missing userId and filter
queryFn: () => fetchUserPosts(userId, filter),
});
}
// CORRECT - All dependencies in key
function UserPosts({ userId, filter }: Props) {
const { data } = useQuery({
queryKey: ['posts', userId, filter], // ✅
queryFn: () => fetchUserPosts(userId, filter),
});
}
```
**❌ Don't Mutate Query Data Directly:**
```tsx
// WRONG - Mutating cached data
const { data } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos });
data.push(newTodo); // ❌ Mutates cache directly
// CORRECT - Use setQueryData
queryClient.setQueryData(['todos'], (old = []) => [...old, newTodo]); // ✅
```
---
## Summary
TanStack Query is the industry-standard solution for server-state management in React applications. Use it for API data fetching, caching, synchronization, and real-time updates. It eliminates manual state management boilerplate and provides powerful features like automatic background refetching, optimistic updates, pagination, and intelligent cache management.
**Key Takeaways:**
- Use `useQuery` for fetching data with automatic caching
- Use `useMutation` for create/update/delete operations
- Query keys are the foundation of cache management
- Invalidate queries after mutations to keep UI in sync
- Leverage optimistic updates for instant UI feedback
- Use `useInfiniteQuery` for pagination and infinite scroll
- Combine with Zustand for client-state management
- Integrate seamlessly with tRPC, REST, and GraphQL
- Type everything with TypeScript for full type safety
- Test with MSW for realistic API mocking
**Progressive Loading Pattern:**
- **Entry Point**: Quick start and basic setup
- **Intermediate**: Queries, mutations, and cache management
- **Advanced**: Optimistic updates, SSR, integrations, and performance
For additional resources, visit the [official documentation](https://tanstack.com/query/latest).
## Related Skills
When using Tanstack Query, these skills enhance your workflow:
- **react**: React hooks and patterns for integrating TanStack Query
- **nextjs**: TanStack Query with Next.js App Router and Server Components
- **zustand**: Complementary client-state management (use together for hybrid state)
- **test-driven-development**: Testing queries, mutations, and cache behavior
[Full documentation available in these skills if deployed in your bundle]