TanStack Query in React
What Is TanStack Query?
TanStack Query is a powerful data-fetching library for React that simplifies
how we:
Fetch data from APIs
Handle loading and error states
Cache and sync data
Automatically refetch updated data
❓ Why Do We Need TanStack Query?
Let’s start with a common real-world problem
Imagine This Use Case:
You are building a React app that:
Fetches a list of users from an API
Displays a loading spinner while data is loading
Shows an error message if something goes wrong
Refreshes the data when you go back to the page
Avoids re-fetching if data is already available
Now let’s try doing this using plain useEffect and useState.
Using useEffect + useState
jsx
import React, { useEffect, useState } from 'react';
import axios from 'axios';
function Users() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
useEffect(() => {
const fetchUsers = async () => {
setLoading(true);
try {
const response = await
axios.get('https://jsonplaceholder.typicode.com/users');
setUsers(response.data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUsers();
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
export default Users;
Problems With useEffect
Issue Explanation
❌ Too much boilerplate You write the same useEffect, useState,
try-catch, loading every time.
❌ No caching If you revisit the page, the API is called again,
wasting time and data.
❌ No auto refetching If data changes on the server, you won't know
unless you reload manually.
❌ Difficult to manage You need to handle page state and fetch logic
pagination/infinite scroll manually.
❌ Mutation handling is Updating/deleting requires a completely
separate different flow.
❌ No retry mechanism If a request fails, it doesn’t retry automatically.
✅ Solution: TanStack Query
TanStack Query helps you manage server state like a pro.
What is Server State?
Data that lives on a remote server (e.g. API).
Needs fetching, caching, updating, syncing.
Advantages of TanStack Query
Feature What It Does
✅ useQuery hook Simple hook to fetch & manage data
✅ Auto loading/error state No manual state management
✅ Built-in caching No need to fetch again if data is fresh
✅ Background refetching Refreshes data when tab is focused again
✅ Automatic retries Retries failed requests automatically
✅ Devtools See cache, queries, retries, etc. visually
✅ Mutation hooks Easily handle POST, PUT, DELETE with useMutation
✅ Pagination/infinite scroll Built-in helpers
✅ Example: Same Use Case with TanStack Query
Step 1: Install
bash
npm install @tanstack/react-query
Step 2: Set Up Provider
jsx
// main.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { QueryClient, QueryClientProvider } from
'@tanstack/react-query';
const queryClient = new QueryClient();
ReactDOM.createRoot(document.getElementById('root')).
render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
);
Step 3: Use useQuery to Fetch Users
jsx
// Users.jsx
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
function fetchUsers() {
return
axios.get('https://jsonplaceholder.typicode.com/users
').then(res => res.data);
}
function Users() {
const { data, isLoading, isError, error } =
useQuery({
queryKey: ['users'],
queryFn: fetchUsers
});
if (isLoading) return <p>Loading...</p>;
if (isError) return <p>Error: {error.message}</p>;
return (
<ul>
{data.map(user => <li
key={user.id}>{user.name}</li>)}
</ul>
);
}
What Did We Gain?
With useEffect With TanStack Query
Manual state for loading/error Built-in (isLoading, isError)
useEffect required Only need useQuery
No caching Cached result
No refetch on window focus Refetches automatically
No retry Retries failed requests
Need logic for pagination Provided out-of-box helpers
Harder to test Easier, predictable structure
Background Refetching
js
useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
staleTime: 1000 * 60 * 5, // 5 minutes
refetchOnWindowFocus: true,
});
➡ Data is considered “fresh” for 5 mins. After that, it can be refetched
automatically.
Summary Table
Concept Explanation
useQuery React Query hook to fetch API data
queryKey Name for caching and identifying the query
queryFn The actual function that fetches data
isLoading Tells if the data is still being loaded
isError Tells if any error occurred
data The final result of the API fetch