Кэширование на уровне компонентов

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

Ключевая особенность механизма — формирование уникального ключа кэша на основе имени endpoint и переданных аргументов. Один и тот же useGetUserQuery(5) в разных компонентах будет обращаться к одному и тому же сегменту кэша.

Если несколько компонентов используют идентичный запрос, RTK Query не выполняет повторный сетевой запрос, а разделяет уже загруженные данные между всеми подписчиками.

Кэш структурно можно представить как:

  • endpointName
  • serialized arguments
  • cached result
  • список подписчиков

При этом изменение аргументов автоматически переключает компонент на другой slice кэша.

Подписка компонента на данные

Каждый вызов useQuery создаёт подписку на конкретный cache entry. Пока компонент смонтирован, подписка активна. Как только компонент размонтируется, подписка удаляется.

Важно, что удаление подписки не означает немедленного удаления данных из кэша. Это управляется параметром keepUnusedDataFor, который определяет время жизни данных без активных подписчиков.

const { data } = api.useGetUserQuery(userId)

Компонент становится наблюдателем конкретного cache entry. Любое обновление данных в кэше автоматически вызывает перерендер всех подписанных компонентов.

Разделение данных между компонентами

RTK Query устраняет необходимость ручного поднятия состояния (lifting state up). Несколько компонентов могут независимо использовать один и тот же endpoint, не зная друг о друге.

Пример поведения:

  • Компонент A вызывает useGetPostQuery(1)
  • Компонент B вызывает useGetPostQuery(1)
  • Выполняется один HTTP-запрос
  • Оба компонента получают один и тот же кешированный результат

При этом система подписок обеспечивает синхронное обновление данных во всех местах использования.

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

Когда последний компонент, использующий конкретный cache entry, размонтируется, RTK Query не удаляет данные мгновенно. Вместо этого запускается таймер очистки.

Параметр:

keepUnusedDataFor: 60

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

Это поведение позволяет:

  • быстро возвращать данные при повторном входе на экран
  • избегать лишних сетевых запросов
  • поддерживать UX без «пустых состояний»

Контроль перезапросов при монтировании

Компонент может инициировать повторную загрузку данных при повторном монтировании через настройки:

  • refetchOnMountOrArgChange
  • refetchOnFocus
  • refetchOnReconnect

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

Особенно важен сценарий:

useGetUserQuery(id, {
  refetchOnMountOrArgChange: true
})

В этом случае компонент становится источником актуализации данных при каждом появлении в дереве.

Изоляция кэша на уровне аргументов

RTK Query строго разделяет данные по аргументам. Даже небольшое отличие приводит к новому cache entry.

useGetUsersQuery({ page: 1 })
useGetUsersQuery({ page: 2 })

Это два независимых состояния кэша.

На уровне компонентов это означает, что pagination, фильтры и сортировки автоматически становятся частью кэш-ключа, если передаются как аргументы endpoint.

Оптимизация через selectFromResult

На уровне компонентов возможна тонкая оптимизация рендеров с помощью selectFromResult. Этот механизм позволяет подписываться не на весь результат запроса, а на его часть.

const { username } = api.useGetUserQuery(id, {
  selectFromResult: ({ data }) => ({
    username: data?.username
  })
})

В этом случае компонент перерисовывается только при изменении username, даже если остальная часть объекта data обновилась.

Это особенно важно при:

  • больших JSON-ответах
  • частых фоновых обновлениях
  • списках с частичной динамикой

Поведение при одинаковых запросах в разных состояниях UI

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

  • модальные окна
  • табы
  • разные страницы с одинаковыми параметрами

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

Реактивность кэша и подписки

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

При этом обновление может происходить:

  • через повторный запрос
  • через mutation с invalidatesTags
  • через updateQueryData
  • через optimistic updates

Компонент не различает источник обновления, он реагирует только на изменение cache entry.

Влияние параметра skip на кэширование

Компонент может временно отключить подписку через skip:

useGetUserQuery(id, { skip: !id })

В этом состоянии:

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

Однако данные, уже находящиеся в кэше, остаются доступными для других компонентов.

Повторное использование кэша при возвращении компонента

При повторном монтировании компонента RTK Query проверяет:

  • существует ли cache entry
  • не истёк ли keepUnusedDataFor
  • нужно ли выполнять refetch согласно стратегиям обновления

Если данные ещё актуальны, компонент мгновенно получает их без сетевого запроса, что создаёт ощущение локального состояния.

Взаимодействие нескольких экземпляров одного компонента

Если один и тот же компонент используется несколько раз на странице, каждый экземпляр создаёт собственную подписку, но все они привязаны к одному cache entry при совпадении аргументов.

Это приводит к:

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

Гранулярность обновлений и ререндеров

RTK Query не перерисовывает весь компонент при любом изменении кэша. Перерисовка происходит только если изменился результат подписки.

Комбинация с selectFromResult позволяет добиться максимально узкой реактивности, когда изменение в одном поле не затрагивает весь UI.

Роль аргументов как источника истины

На уровне компонентов аргументы запроса становятся декларативным описанием данных. Компонент не управляет кэшем напрямую — он описывает:

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

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