Опции staleTime и cacheTime

TanStack Query хранит результаты запросов в кэше и управляет их жизненным циклом автоматически. Две ключевые настройки, определяющие поведение кэша:

  • staleTime
  • cacheTime (в новых версиях также используется название gcTime)

Несмотря на внешнюю схожесть, эти параметры решают совершенно разные задачи:

Опция Назначение
staleTime Определяет, как долго данные считаются свежими
cacheTime Определяет, как долго неиспользуемый кэш хранится в памяти

Непонимание различий между ними — одна из самых распространённых причин лишних запросов, устаревших данных и проблем с производительностью.


Опция staleTime

Что такое «свежие» данные

После успешного выполнения запроса TanStack Query помещает данные в кэш. Сразу после получения они считаются свежими (fresh).

Пока данные свежие:

  • TanStack Query не будет автоматически выполнять повторный запрос;
  • повторное открытие компонента не приведёт к сетевому запросу;
  • фокусировка окна не вызовет рефетч;
  • повторный рендер использует кэш.

Когда время staleTime истекает, данные становятся устаревшими (stale).

Важно понимать: устаревшие данные не удаляются. Они продолжают использоваться, но библиотека получает право обновлять их автоматически.


Значение по умолчанию

По умолчанию:

staleTime: 0

Это означает:

  • данные становятся устаревшими сразу после получения;
  • TanStack Query может выполнять фоновое обновление практически при любом удобном событии.

Пример:

useQuery({
    queryKey: ['posts'],
    queryFn: fetchPosts
})

Поведение:

  1. Данные загружаются.
  2. Сразу становятся stale.
  3. При возврате на вкладку браузера запрос может выполниться снова.
  4. При повторном монтировании компонента тоже возможен рефетч.

Как работает staleTime

Пример с временной шкалой

useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    staleTime: 10000
})

Значение:

10000

— это 10 секунд.

Поведение:

Время Состояние
0 сек Запрос выполнен
0–10 сек Данные fresh
После 10 сек Данные stale

Пока данные свежие:

const { data } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    staleTime: 10000
})

TanStack Query будет возвращать кэш без повторного запроса.


Поведение staleTime при повторном монтировании

Без staleTime

useQuery({
    queryKey: ['profile'],
    queryFn: fetchProfile
})

Сценарий:

  1. Компонент открылся.
  2. Данные загрузились.
  3. Компонент размонтировался.
  4. Компонент открылся снова.

Так как данные уже stale, библиотека выполнит повторный запрос.


Со staleTime

useQuery({
    queryKey: ['profile'],
    queryFn: fetchProfile,
    staleTime: 60000
})

Теперь данные свежие в течение минуты.

Если компонент откроется повторно в пределах 60 секунд:

  • запрос не выполнится;
  • данные будут взяты из кэша;
  • интерфейс отобразится мгновенно.

staleTime и refetchOnWindowFocus

По умолчанию TanStack Query обновляет stale-запросы при возврате фокуса окна браузера.

Пример:

useQuery({
    queryKey: ['notifications'],
    queryFn: fetchNotifications
})

Сценарий:

  1. Пользователь переключился на другую вкладку.
  2. Вернулся обратно.
  3. Данные stale.
  4. Выполняется refetch.

Влияние staleTime

useQuery({
    queryKey: ['notifications'],
    queryFn: fetchNotifications,
    staleTime: 300000
})

Теперь данные считаются свежими 5 минут.

В течение этого времени возврат на вкладку не вызовет новый запрос.


staleTime и refetchOnMount

Опция refetchOnMount определяет, нужно ли обновлять запрос при монтировании компонента.

По умолчанию:

refetchOnMount: true

Но запрос выполняется только если данные stale.

Пример:

useQuery({
    queryKey: ['products'],
    queryFn: fetchProducts,
    staleTime: 20000
})

Если компонент повторно смонтируется через 5 секунд:

  • данные fresh;
  • запрос не выполняется.

Если через 25 секунд:

  • данные stale;
  • TanStack Query выполнит refetch.

staleTime: Infinity

Иногда данные практически никогда не меняются.

Например:

  • список стран;
  • языки;
  • настройки приложения;
  • статические справочники.

В таких случаях используют:

staleTime: Infinity

Пример:

useQuery({
    queryKey: ['countries'],
    queryFn: fetchCountries,
    staleTime: Infinity
})

Теперь данные:

  • никогда не становятся stale;
  • никогда автоматически не обновляются;
  • обновятся только вручную.

Ручное обновление при staleTime: Infinity

const queryClient = useQueryClient()

queryClient.invalidateQueries({
    queryKey: ['countries']
})

После инвалидирования:

  • запрос становится stale;
  • TanStack Query может выполнить refetch.

Практические сценарии staleTime

Часто изменяемые данные

Например:

  • чат;
  • уведомления;
  • биржевые котировки.
staleTime: 0

или:

staleTime: 5000

Умеренно изменяемые данные

Например:

  • список товаров;
  • профили пользователей;
  • комментарии.
staleTime: 60000

Почти статические данные

Например:

  • справочники;
  • конфигурации;
  • категории.
staleTime: Infinity

Опция cacheTime

Основная задача cacheTime

cacheTime отвечает не за свежесть данных, а за время хранения неиспользуемого кэша.

Ключевая идея:

  • пока запрос используется хотя бы одним компонентом — кэш активен;
  • когда последний компонент размонтировался — запрос становится неактивным (inactive);
  • после этого запускается таймер cacheTime.

Значение по умолчанию

По умолчанию:

cacheTime: 1000 * 60 * 5

То есть:

5 минут

Как работает cacheTime

Пример

useQuery({
    queryKey: ['articles'],
    queryFn: fetchArticles,
    cacheTime: 300000
})

Сценарий:

  1. Компонент открылся.
  2. Запрос выполнился.
  3. Компонент закрылся.
  4. Запрос стал inactive.
  5. TanStack Query ждёт 5 минут.
  6. Если запрос снова понадобится — кэш используется повторно.
  7. Если нет — кэш удаляется из памяти.

Важное различие между staleTime и cacheTime

staleTime

Управляет:

Можно ли считать данные актуальными?

cacheTime

Управляет:

Можно ли хранить данные в памяти?

Визуальное сравнение

staleTime

fresh → stale

Данные продолжают существовать.


cacheTime

inactive → garbage collection

Данные полностью удаляются из кэша.


Пример совместной работы staleTime и cacheTime

useQuery({
    queryKey: ['me'],
    queryFn: fetchCurrentUser,
    staleTime: 60000,
    cacheTime: 300000
})

Поведение:

Время Состояние
0–60 сек Данные fresh
После 60 сек Данные stale
После размонтирования Запускается cacheTime
Через 5 минут inactive Кэш удаляется

Что происходит после удаления кэша

Если cacheTime истёк:

  • запрос полностью удаляется;
  • данные исчезают из памяти;
  • следующий mount выполнит полноценный сетевой запрос.

cacheTime: Infinity

Иногда кэш нужно хранить постоянно.

cacheTime: Infinity

Пример:

useQuery({
    queryKey: ['settings'],
    queryFn: fetchSettings,
    cacheTime: Infinity
})

Теперь кэш:

  • никогда автоматически не удаляется;
  • остаётся в памяти всё время жизни приложения.

Риски слишком большого cacheTime

Слишком долгий cacheTime может привести к:

  • росту потребления памяти;
  • накоплению большого количества старых запросов;
  • ухудшению производительности.

Особенно опасно:

cacheTime: Infinity

для:

  • бесконечных списков;
  • больших массивов;
  • тяжёлых API-ответов.

staleTime больше cacheTime

Это распространённая ошибка конфигурации.

Пример:

useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    staleTime: 600000,
    cacheTime: 60000
})

Что происходит:

  • данные должны быть fresh 10 минут;
  • но кэш удалится через 1 минуту после inactive-состояния.

В результате:

  • данные исчезнут раньше, чем устареют;
  • следующий mount всё равно вызовет новый запрос.

Рекомендуемое правило

Обычно:

cacheTime >= staleTime

Это позволяет:

  • сохранять свежие данные в памяти;
  • избегать лишних запросов;
  • эффективно использовать кэш.

Глобальная настройка staleTime и cacheTime

Настройка QueryClient

const queryClient = new QueryClient({
    defaultOptions: {
        queries: {
            staleTime: 60000,
            cacheTime: 300000
        }
    }
})

Теперь все запросы по умолчанию используют эти значения.


Переопределение в конкретном запросе

useQuery({
    queryKey: ['posts'],
    queryFn: fetchPosts,
    staleTime: 10000
})

Локальная настройка имеет приоритет.


Поведение при навигации между страницами

Маленький staleTime

staleTime: 0

При переходах между страницами:

  • часто происходят refetch-запросы;
  • сеть используется активнее;
  • данные более актуальны.

Большой staleTime

staleTime: 300000

Переходы становятся почти мгновенными:

  • данные берутся из кэша;
  • сетевых запросов меньше;
  • интерфейс работает быстрее.

staleTime и UX

Правильно подобранный staleTime сильно влияет на пользовательский опыт.

Слишком маленький staleTime

Проблемы:

  • постоянные загрузки;
  • мерцания интерфейса;
  • лишние HTTP-запросы;
  • повышенная нагрузка на API.

Слишком большой staleTime

Проблемы:

  • отображение устаревших данных;
  • несинхронность интерфейса;
  • пользователь видит старую информацию.

Подход к выбору staleTime

Нужно оценивать:

  • как часто изменяются данные;
  • насколько критична актуальность;
  • насколько дорог запрос;
  • насколько важна скорость интерфейса.

Примеры хороших значений

Тип данных staleTime
Чат 0–5 сек
Уведомления 5–30 сек
Список товаров 1–5 мин
Профиль пользователя 5–30 мин
Справочники Infinity

Devtools и наблюдение за staleTime

TanStack Query Devtools позволяют видеть:

  • fresh/stale состояние;
  • время жизни кэша;
  • inactive queries;
  • удаление запросов;
  • refetch в реальном времени.

Пример подключения:

import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

function App() {
    return (
        <>
            <Routes />
            <ReactQueryDevtools initialIsOpen={false} />
        </>
    )
}

Изменение cacheTime в runtime

Можно программно управлять запросами:

queryClient.removeQueries({
    queryKey: ['posts']
})

Полное удаление:

  • очищает кэш;
  • сбрасывает состояние;
  • заставляет следующий запрос загрузиться заново.

Инвалидация и staleTime

Инвалидация игнорирует staleTime.

Пример:

queryClient.invalidateQueries({
    queryKey: ['todos']
})

Даже если данные были fresh:

staleTime: Infinity

они становятся stale вручную.


staleTime не запрещает refetch

Важно понимать:

staleTime

не блокирует refetch полностью.

Запрос всё ещё можно:

  • инвалидировать;
  • обновить вручную;
  • refetch через API;
  • обновить через mutation.

Ручной refetch

const { refetch } = useQuery({
    queryKey: ['stats'],
    queryFn: fetchStats,
    staleTime: Infinity
})

await refetch()

Даже при бесконечном staleTime запрос обновится.


Влияние cacheTime на память приложения

Каждый query хранит:

  • данные;
  • статус;
  • метаданные;
  • timestamps;
  • observers.

Большое количество запросов с длинным cacheTime может существенно увеличить объём памяти.

Особенно в:

  • CRM-системах;
  • админках;
  • аналитических панелях;
  • SPA с долгой жизнью вкладки.

Типичная конфигурация для большинства приложений

const queryClient = new QueryClient({
    defaultOptions: {
        queries: {
            staleTime: 60000,
            cacheTime: 300000,
            refetchOnWindowFocus: false
        }
    }
})

Такой подход:

  • уменьшает количество запросов;
  • делает интерфейс стабильнее;
  • сохраняет достаточно актуальные данные;
  • снижает нагрузку на сервер.