SyntaxStudy
Sign Up
React TanStack Query: Caching and Background Refetching
React Beginner 1 min read

TanStack Query: Caching and Background Refetching

TanStack Query (formerly React Query) provides a complete server-state management solution. It handles caching, deduplication, background refetching, stale-while-revalidate behaviour, and error retries out of the box. The mental model is simple: every piece of remote data is identified by a query key, and the library manages the lifecycle of fetching and caching that data. The useQuery hook accepts a query key array and an async query function. The library caches the result under that key. Subsequent components that use the same key share the cached data and do not trigger duplicate network requests. When data becomes stale (by default after zero milliseconds) a background refetch is triggered on focus or reconnect to ensure the UI stays fresh. useMutation is the counterpart for writes: it wraps an async function that sends data to the server. Its onSuccess and onError callbacks let you invalidate relevant query keys — causing the affected queries to refetch — or apply optimistic updates that show the expected result before the server confirms.
Example
// npm install @tanstack/react-query

import {
    QueryClient, QueryClientProvider,
    useQuery, useMutation, useQueryClient,
} from '@tanstack/react-query';

const queryClient = new QueryClient();

// ── Wrap the app ───────────────────────────────────────────────────────────
export function App() {
    return (
        <QueryClientProvider client={queryClient}>
            <Todos />
        </QueryClientProvider>
    );
}

// ── Fetch todos ────────────────────────────────────────────────────────────
async function fetchTodos() {
    const res = await fetch('/api/todos');
    if (!res.ok) throw new Error('Failed to fetch');
    return res.json();
}

async function addTodo(title) {
    const res = await fetch('/api/todos', {
        method:  'POST',
        headers: { 'Content-Type': 'application/json' },
        body:    JSON.stringify({ title, completed: false }),
    });
    if (!res.ok) throw new Error('Failed to add');
    return res.json();
}

function Todos() {
    const qc = useQueryClient();

    const { data: todos = [], isLoading, isError } = useQuery({
        queryKey: ['todos'],
        queryFn:  fetchTodos,
    });

    const mutation = useMutation({
        mutationFn: addTodo,
        onSuccess: () => qc.invalidateQueries({ queryKey: ['todos'] }),
    });

    if (isLoading) return <p>Loading…</p>;
    if (isError)   return <p>Something went wrong.</p>;

    return (
        <div>
            <button onClick={() => mutation.mutate('New todo')}>Add Todo</button>
            <ul>
                {todos.map(t => <li key={t.id}>{t.title}</li>)}
            </ul>
        </div>
    );
}