The TanStack Query (formerly React Query) caching system is designed to efficiently manage server state, ensuring data consistency and minimal refetching. Here’s an overview of its cache structure and key concepts:

Cache Structure Overview

At its core, the TanStack Query cache is organized around query keys. The cache holds metadata, the query result, and related information for each query key. The structure can be thought of as follows:

Cache
└── Query Key 1 (Array or String)
    β”œβ”€β”€ Query Data (Result from server)
    β”œβ”€β”€ Metadata
    β”‚   β”œβ”€β”€ Status (idle, loading, error, success)
    β”‚   β”œβ”€β”€ Fetch Status (fetching, paused, stale, etc.)
    β”‚   β”œβ”€β”€ Is Stale (true/false)
    β”‚   β”œβ”€β”€ Updated At (timestamp)
    β”‚   β”œβ”€β”€ Observer Count (active subscriptions)
    β”‚   β”œβ”€β”€ Retry Attempts
    β”‚   └── Error (if any)
    └── Configuration
        β”œβ”€β”€ Cache Time (ms)
        β”œβ”€β”€ Stale Time (ms)
        β”œβ”€β”€ Retry Config
        └── Other Options

Each query key serves as a unique identifier for a cached query, and the associated metadata and configuration define how it behaves.


Key Components of the Cache

  1. Query Keys:
    • Unique identifiers for each piece of cached data.
    • Can be simple strings ('users') or nested arrays (['users', { page: 1 }]) for complex queries.
useQuery(['users', { page: 1 }], fetchUsers);
  1. Query Data:
    • The actual data fetched from the server (or provided manually).
    • Stored until the query is garbage-collected (based on cacheTime).
  2. Query Metadata:
    • Stores the state of the query:
      • status: Indicates if the query is idle, loading, error, or success.
      • fetchStatus: Shows whether data is actively being fetched (fetching, paused, etc.).
      • isStale: Marks if the data is considered stale (based on staleTime).
  3. Observers:
    • Each query can have multiple observers (e.g., useQuery instances).
    • A query stays active as long as it has active observers.
    • Observers receive updates when the query data changes.
  4. Stale Time:
    • Time (in ms) before the data is marked as stale.
    • Stale queries trigger a refetch when accessed or under specific conditions.
  5. Cache Time:
    • Time (in ms) a query remains in the cache after it becomes inactive (i.e., has no active observers).
  6. Retry Logic:
    • Configurable retry attempts and delay for failed queries.
    • Managed by the retry and retryDelay options.

How the Cache Updates

  1. Fetching Data:
    • When a query is fetched, the result is cached under the associated query key.
    • Metadata is updated to reflect the new status, fetchStatus, and updatedAt timestamp.
  2. Invalidation:
    • Queries can be manually invalidated using queryClient.invalidateQueries(queryKey).
    • Invalidated queries are marked as stale and refetched when accessed.
  3. Garbage Collection:
    • When a query has no active observers, it enters an inactive state.
    • After the cacheTime expires, it is removed from the cache.
  4. Optimistic Updates:
    • You can update cached data immediately using queryClient.setQueryData, even before the server response.
queryClient.setQueryData(['todos'], (old) => [...old, newTodo]);

Customizing Cache Behavior

TanStack Query provides configuration options to control caching behavior:

useQuery(['users'], fetchUsers, {
  staleTime: 10000,  // Data remains fresh for 10 seconds
  cacheTime: 300000, // Cache persists for 5 minutes after inactive
  refetchOnWindowFocus: false, // Disable refetching on window focus
  retry: 3, // Retry up to 3 times on failure
  retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 30000), // Exponential backoff
});

QueryClient Cache Access

You can directly access or manipulate the cache using QueryClient:

import { QueryClient } from '@tanstack/react-query';

const queryClient = new QueryClient();

// Access all cached queries
const allQueries = queryClient.getQueryCache().getAll();

// Get a specific query's cached data
const userData = queryClient.getQueryData(['users', { page: 1 }]);

// Manually set cached data
queryClient.setQueryData(['users', { page: 1 }], { name: 'John Doe' });

Visualization of Cache Operations

  1. Fetch data:
    • Key: ['users']
    • Data: [ { id: 1, name: 'John' }, { id: 2, name: 'Jane' } ]
    • Metadata:
      • status: 'success'
      • fetchStatus: 'idle'
      • isStale: false
      • updatedAt: 1672519200000
  2. Modify the cache manually:
queryClient.setQueryData(['users'], (old) => [...old, { id: 3, name: 'Alice' }]);
Ilya Elias S @reactima
React/TS/Node/Python/Golang Coder
πŸ‡―πŸ‡΅ Japan Permanent Resident
Used to live in πŸ‡ΊπŸ‡¦πŸ‡ΊπŸ‡ΈπŸ‡ΈπŸ‡¬πŸ‡­πŸ‡°πŸ‡¬πŸ‡ͺπŸ‡³πŸ‡±
Interested to discuss the above or looking for a partner to work on Data Mining, Recruitment, B2B Lead Generation and/or Outbound SaaS related projects?
Feel free to ping me to exchange ideas or request a consultation!