Время жизни кэша

В RTK Query кэширование не ограничивается простым хранением данных в памяти. Каждый фрагмент состояния запроса существует в рамках управляемого жизненного цикла, который определяет, как долго данные остаются актуальными, когда они считаются устаревшими и в какой момент могут быть удалены из хранилища.

Ключевая особенность механизма заключается в том, что кэш привязан не только к данным, но и к подпискам на них. Это означает, что время жизни кэша определяется сочетанием активности компонентов, настроек API-сервиса и внутренних таймеров библиотеки.


Основные параметры, влияющие на жизненный цикл кэша

keepUnusedDataFor

Главный параметр управления временем жизни кэша — keepUnusedDataFor. Он задаётся на уровне API-сервиса или для конкретного endpoint.

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const api = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  keepUnusedDataFor: 60,
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => '/posts',
    }),
  }),
});

Значение задаётся в секундах и определяет, как долго данные остаются в кэше после того, как последний подписчик был удалён.

Ключевая логика:

  • пока есть активные подписчики — кэш живёт независимо от этого параметра
  • после удаления последнего подписчика запускается таймер
  • по истечении времени данные удаляются из store

Подписки как основной механизм удержания кэша

Каждый вызов useQuery создаёт подписку на конкретный cache key. RTK Query группирует запросы по следующим признакам:

  • endpoint name
  • аргументы запроса

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

const { data } = useGetPostsQuery();
const { data } = useGetPostsQuery();

Обе подписки используют один и тот же cache entry.

Влияние подписок на время жизни

Пока хотя бы одна подписка активна:

  • данные не удаляются
  • keepUnusedDataFor не запускается
  • возможен фоновой рефетч

После исчезновения последней подписки:

  • запускается отсчёт времени
  • кэш становится кандидатом на удаление

Поведение кэша при размонтировании компонентов

RTK Query тесно связан с жизненным циклом React-компонентов. При размонтировании компонента:

  1. подписка удаляется
  2. проверяется наличие других подписчиков
  3. если подписчиков нет — запускается таймер удаления

Пример сценария:

  • компонент A использует useGetPostsQuery
  • компонент B использует тот же query
  • компонент A размонтируется → кэш остаётся активным
  • компонент B размонтируется → начинается отсчёт keepUnusedDataFor

Различие между актуальностью данных и временем жизни кэша

Важно различать два независимых механизма:

1. Время жизни кэша

Определяет, когда данные будут удалены из store.

2. Устаревание данных (stale)

Определяет, когда данные считаются устаревшими и требуют обновления.

RTK Query не удаляет кэш автоматически при устаревании. Вместо этого он может инициировать refetch при определённых условиях.


refetchOnMount и его влияние на кэш

Параметр refetchOnMount управляет тем, будет ли запрос повторно выполнен при повторном монтировании компонента.

useGetPostsQuery(undefined, {
  refetchOnMountOrArgChange: true,
});

Даже если кэш ещё существует:

  • данные могут быть использованы из store
  • но одновременно может выполняться новый запрос

Это не влияет напрямую на время жизни кэша, но влияет на его обновление.


Глобальная настройка времени жизни

В createApi можно задать базовое значение:

keepUnusedDataFor: 30

Это значение применяется ко всем endpoint, если не переопределено локально.

Локальное значение имеет приоритет:

getPosts: builder.query({
  query: () => '/posts',
  keepUnusedDataFor: 120,
})

Сценарии поведения кэша

Сценарий 1: Однократное использование

  • запрос выполнен
  • компонент размонтирован
  • подписчиков нет
  • запускается таймер
  • по истечении времени кэш удаляется

Сценарий 2: Переиспользование кэша

  • запрос выполнен в компоненте A
  • компонент B монтируется с тем же query
  • используется существующий cache entry
  • новый запрос не выполняется

Сценарий 3: Постоянно активные подписки

  • компонент с запросом всегда смонтирован
  • подписка не удаляется
  • keepUnusedDataFor никогда не запускается

Очистка кэша при ручном вмешательстве

RTK Query позволяет управлять временем жизни кэша через invalidateTags и resetApiState.

resetApiState

Полная очистка состояния API:

dispatch(api.util.resetApiState());

После вызова:

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

Инвалидация и влияние на кэш

Инвалидация тегов не удаляет данные сразу. Она помечает их как устаревшие.

invalidatesTags: ['Posts']

После инвалидации:

  • данные остаются в кэше
  • при следующем использовании возможен refetch
  • время жизни кэша не обнуляется автоматически

Внутренний механизм таймера удаления

После исчезновения последнего подписчика RTK Query:

  1. фиксирует timestamp освобождения

  2. запускает внутренний таймер

  3. по истечении keepUnusedDataFor:

    • удаляет cache entry
    • очищает связанные метаданные
    • освобождает память

Если до истечения таймера появляется новая подписка:

  • таймер отменяется
  • кэш снова становится активным

Влияние частоты переключения компонентов

При частом монтировании и размонтировании компонентов:

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

Данные можно условно разделить:

  • статические (справочники) — большой TTL
  • динамические (ленты, сообщения) — малый TTL
  • критически актуальные (статус, баланс) — почти нулевой TTL

Связь времени жизни кэша и производительности

RTK Query оптимизирует работу за счёт удержания кэша в памяти. Время жизни напрямую влияет на:

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

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


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

Каждая комбинация аргументов создаёт отдельный cache key.

useGetPostsQuery({ page: 1 });
useGetPostsQuery({ page: 2 });

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

  • два независимых кэша
  • два независимых таймера жизни
  • независимые подписки

Удаление одного не влияет на другой.


Роль garbage collection внутри RTK Query

Механизм очистки кэша работает как специализированный garbage collector:

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

Основное отличие от классического GC заключается в том, что он привязан к бизнес-логике запросов, а не к объектам JavaScript напрямую.