Tanstack Query Explained: A Complete Handbook
TanStack Query is useful for data fetching, caching, synchronizing, and updating server state in your React applications.
Why TanStack Query is Useful
Data Fetching & Caching: Automatically handles caching of data so that you don't have to fetch the same data multiple times.
Automatic Re-fetching: Data is automatically re-fetched when it becomes stale or if the app regains focus after going into the background.
Mutation Management: Handles mutation (POST, PUT, DELETE requests) in an optimized way, providing options to revalidate data after mutations.
Error Handling: Automatically retries fetching on errors, provides error states, and lets you handle retries.
Stale Time & Refetch: You can configure how long data stays fresh and when it needs to be re-fetched.
Setup TanStack Query in a React App
1. Install Dependencies
You'll need to install @tanstack/react-query
and axios
for making API requests.
npm install @tanstack/react-query axios
2. Set up the QueryClientProvider
In your main index.js
or App.js
, wrap your app with the QueryClientProvider
and pass the queryClient
instance.
import React from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import ReactDOM from "react-dom";
import App from "./App";
// Create a client
const queryClient = new QueryClient();
ReactDOM.render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>,
document.getElementById("root")
);
Fetching Data with TanStack Query
Let's say you want to fetch a list of products for your e-commerce app. You can use the useQuery
hook for fetching and caching data.
3. Fetching Products Example
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
// API call function
const fetchProducts = async () => {
const { data } = await axios.get("https://api.example.com/products");
return data;
};
const ProductsList = () => {
const { isLoading, error, data: products } = useQuery({
queryKey: ["products"],
queryFn: fetchProducts,
staleTime: 5 * 60 * 1000, // Data stays fresh for 5 minutes
});
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Products</h1>
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</div>
);
};
export default ProductsList;
Mutation (Adding to Cart) Example
For adding an item to the cart (a POST request), you can use the useMutation
hook. After a successful mutation, you may want to re-fetch the cart data to keep it up-to-date.
4. Mutating Data (Add to Cart) Example
import { useMutation, useQueryClient } from "@tanstack/react-query";
import axios from "axios";
// Function to add a product to the cart
const addToCart = async (productId) => {
const { data } = await axios.post("https://api.example.com/cart", { productId });
return data;
};
const Product = ({ productId }) => {
const queryClient = useQueryClient();
const { mutate, isLoading, error } = useMutation({
mutationFn: addToCart,
onSuccess: () => {
// Invalidate the cart query to refetch the cart data
queryClient.invalidateQueries(["cart"]);
},
});
if (isLoading) return <p>Adding to cart...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<button onClick={() => mutate(productId)}>Add to Cart</button>
);
};
Refetching Data (Revalidating)
When you want to revalidate or refetch data, you can use the refetch
function returned by the useQuery
hook. Additionally, data is refetched when it becomes stale, or you can configure it to refetch when the window regains focus.
const { data: cartItems, refetch } = useQuery({
queryKey: ["cart"],
queryFn: fetchCartItems,
staleTime: 1000 * 60, // Cart stays fresh for 1 minute
});
return (
<div>
<h1>Cart Items</h1>
<button onClick={refetch}>Refetch Cart</button>
{cartItems?.map((item) => (
<p key={item.id}>{item.name}</p>
))}
</div>
);
Additional Features
Polling/Refetch Interval: You can poll data at specific intervals to keep it updated.
useQuery({ queryKey: ["products"], queryFn: fetchProducts, refetchInterval: 10000, // Refetch every 10 seconds });
Invalidate Queries: After a successful mutation, invalidate specific queries to refetch them.
queryClient.invalidateQueries(["cart"]);
Optimistic Updates: You can optimistically update the UI before waiting for the server response.
useMutation({ mutationFn: addToCart, onMutate: async (newItem) => { await queryClient.cancelQueries(["cart"]); const previousCart = queryClient.getQueryData(["cart"]); queryClient.setQueryData(["cart"], (old) => [...old, newItem]); return { previousCart }; }, onError: (err, newItem, context) => { queryClient.setQueryData(["cart"], context.previousCart); }, onSuccess: () => { queryClient.invalidateQueries(["cart"]); }, });
Conclusion
TanStack Query simplifies the process of fetching, caching, mutating, and managing server-side data in a React app. In an e-commerce app, you can use it to:
Fetch and display products.
Manage cart actions (add to cart, update cart, etc.).
Handle mutations (adding/removing items from the cart) with automatic re-fetching.
Use features like caching, error handling, optimistic updates, and automatic refetching.
This approach helps you focus on the UI and business logic without worrying about data fetching boilerplate code.