Data Fetching
TanStack Query
Master TanStack Query (React Query), covering QueryClient setups, useQuery caching, useMutation server updates, and cache invalidation.
1. Learning Objectives
In this lesson, you will master TanStack Query (formerly React Query) for managing server state. By the end of this topic, you will be able to:
- Configure the
QueryClientand wrap applications in theQueryClientProvider. - Fetch cached server data declaratively using the
useQueryHook. - Mutate server state and handle request lifecycles using
useMutation. - Synchronize state by invalidating query caches using
queryClient.invalidateQueries. - Differentiate between
staleTimeandcacheTimeparameters.
2. Overview
**TanStack Query** (React Query) is a library for fetching, caching, synchronizing, and updating server state in React applications. Instead of managing loading spinners and error states manually using `useEffect` and local state, TanStack Query handles caching, background updates, and stale-while-revalidate data flow automatically.
3. Why This Topic Matters
TanStack Query simplifies data management and improves performance in production:
- Server State vs. Client State: Client state (like dark mode toggles) is owned by the app, while server state (like database records) is remote and can change without the app's knowledge. TanStack Query specializes in keeping this remote data in sync.
- Automatic Caching: Fetched data is cached automatically. If a component requests the same data again, TanStack Query serves the cached copy instantly, then refetches the data in the background to ensure it is up to date.
- Out-of-the-Box Features: Common requirements (like pagination, infinite scrolling, refetch-on-window-focus, and request retries) are supported natively.
4. Real-World Analogy
Think of TanStack Query like **having a personal butler run errands**:
- Without TanStack Query (Manual useEffect): You must drive to the store, check item availability, buy it, and arrange it in your cabinet. If you want a fresh item, you must repeat the entire process from scratch.
- With TanStack Query: You ask the butler (the library) for the morning newspaper (useQuery). If he already has a copy in the house (the cache), he hands it to you immediately (instant load). He then checks the front yard in the background to see if a newer edition has arrived (background refetch), swapping it out silently if available.
5. Core Concepts
Below is a comparison of manual useEffect fetching and TanStack Query:
| Feature | Manual Fetching (useEffect) | TanStack Query (React Query) |
|---|---|---|
| Caching Mechanism | None. Must build manual cache layers or state stores. | Built-in. Caches and syncs queries using unique query keys. |
| Background Updates | Manual implementation required to fetch fresh data. | Automatic updates on window focus, reconnection, or mount. |
| Request Retries | Fails immediately on error unless retries are coded manually. | Automatically retries failed requests 3 times before displaying error. |
| Cache Invalidation | Complex sync logic required to refresh state across pages. | Simple cache invalidation using invalidateQueries(). |
6. Syntax & API Reference
This example shows how to configure a query and mutation in TanStack Query:
7. Visual Diagram
This diagram displays the query cache lifecycles inside TanStack Query:
8. Live Example — Full Working Code
Below is a complete HTML page demonstrating TanStack Query with queries, mutations, and cache invalidation:
What just happened? The application wraps components in QueryClientProvider. The TaskViewer uses the useQuery Hook to fetch and cache task lists. Clicking the add button triggers the useMutation Hook to send new tasks to the server. The mutation's onSuccess callback calls invalidateQueries to mark the cached tasks query as stale, triggering an automatic background refetch and updating the UI instantly.
9. Interactive Playground
Try It Yourself Challenges:
- Change tabs or click outside the page and return to the playground to verify that the query is refetched automatically in the background (triggering the `isFetching` status text).
- Modify the query to set
refetchOnWindowFocus: falseto disable background updates on tab focus.
10. Common Mistakes
| Mistake | Why it happens | Wrong | Correct |
|---|---|---|---|
| Using static query keys for dynamic query parameters | If you query dynamic data (like details pages using a userId parameter) using a static query key (like ['user']), the query client will cache different profiles under the same key. When the ID parameter changes, TanStack Query will continue serving cached data matching the first ID. |
useQuery({ queryKey: ['user'], queryFn: () => getUser(id) }) (static query key) |
useQuery({ queryKey: ['user', id], queryFn: () => getUser(id) }) (dynamic query key) |
| Mutating server state inside query functions | Query functions (`queryFn`) must be pure GET-like requests. Performing POST, PUT, or DELETE mutations inside query functions breaks caching behavior and triggers unexpected state changes. | Performing API updates inside `queryFn` declarations. | Use the useMutation Hook for any operations that modify data on the server. |
11. Best Practices
- Use Dynamic Query Keys: Always include dynamic parameters (like IDs, page numbers, or search queries) in the query key array (e.g. `['todos', userId]`).
- Keep queries read-only: Only use `useQuery` for read operations. Use `useMutation` for any write operations that update data on the server.
- Invalidate caches on mutations: Call `queryClient.invalidateQueries` in the `onSuccess` callback of mutations to trigger automatic background updates.
- Tune staleTime settings: Set custom `staleTime` values (e.g. 5 minutes) for static data to prevent unnecessary background refetches.
12. Browser Compatibility/Requirements
TanStack Query is supported in all modern browsers.
13. Interview Questions
Q1: What is the main difference between `staleTime` and `cacheTime` in TanStack Query?
Answer:
- staleTime: The duration (in milliseconds) before cached data is considered stale. As long as the data is fresh (staleTime has not passed), components will read cached data directly without triggering background refetches.
- cacheTime (gcTime): The duration (in milliseconds) before unused query data is removed from the cache entirely. If a query is not active for this duration, its cached data is garbage collected.
Q2: Why must you invalidate queries after successful mutations? How do you implement this?
Answer: Mutating data on the server makes the local client cache stale. To prevent the UI from displaying outdated information, you must mark affected cache query keys as stale, triggering an automatic background refresh. You implement this by calling `queryClient.invalidateQueries({ queryKey: [...] })` in the mutation's `onSuccess` callback.
14. Debugging Exercise
Identify why the user profile details view does not update when selecting a different user from the sidebar:
Diagnosis: The query key is static (['user_details']). When the userId parameter updates, TanStack Query matches the static key in its cache and returns the data from the first user fetch, ignoring the updated ID.
Fix: Add the dynamic userId parameter to the query key array:
15. Practice Exercises
Exercise 1: Create a query component with cache settings
Build a component named CachedProductCatalog. Fetch a product list using useQuery, setting a staleTime of 30 seconds and a refetchOnWindowFocus configuration of `false`.
16. Scenario-Based Challenge
The Dashboard Metrics Optimization Project:
You are designing a dashboard with widgets displaying financial charts, server logs, and user reports. Some widgets display live data that changes frequently (requires polling every 5 seconds), while other panels display static configuration info. Describe how you would configure TanStack Query's cache settings (using properties like refetchInterval and staleTime) to optimize server load.
17. Quick Quiz
Q1: Which method is called on the queryClient object to mark a cache entry as stale and trigger a refetch?
A) clearQueries()
B) invalidateQueries()
C) deleteQueries()
Answer: B — invalidateQueries() is used to invalidate caches for specified query keys, triggering background updates.
18. Summary & Key Takeaways
- TanStack Query manages caching and synchronization of server state.
- Use the
useQueryHook to fetch and cache remote data. - Include any dynamic parameters (like IDs or search terms) in the query key array.
- Use
useMutationto modify data on the server, and invalidate caches inside theonSuccesscallback.
19. Cheat Sheet
| TanStack Query Element | Purpose |
|---|---|
useQuery({ queryKey, queryFn, ... }) |
Fetches, caches, and syncs remote data. |
useMutation({ mutationFn, onSuccess }) |
Performs write operations and updates data on the server. |
client.invalidateQueries({ queryKey }) |
Invalidates the cache entry for a query key, triggering a background refetch. |