RTK Query привязывает кэширование данных не к отдельным компонентам,
а к результатам запросов, которые затем подписываются компонентами через
хуки. На уровне компонентов это выражается в том, что каждый вызов
useQuery создаёт подписку на конкретный набор данных,
идентифицируемый аргументами запроса, а кэш живёт до тех пор, пока есть
активные подписчики.
Ключевая особенность механизма — формирование уникального ключа кэша
на основе имени endpoint и переданных аргументов. Один и тот же
useGetUserQuery(5) в разных компонентах будет обращаться к
одному и тому же сегменту кэша.
Если несколько компонентов используют идентичный запрос, RTK Query не выполняет повторный сетевой запрос, а разделяет уже загруженные данные между всеми подписчиками.
Кэш структурно можно представить как:
При этом изменение аргументов автоматически переключает компонент на другой slice кэша.
Каждый вызов useQuery создаёт подписку на конкретный
cache entry. Пока компонент смонтирован, подписка активна. Как только
компонент размонтируется, подписка удаляется.
Важно, что удаление подписки не означает немедленного удаления данных
из кэша. Это управляется параметром keepUnusedDataFor,
который определяет время жизни данных без активных подписчиков.
const { data } = api.useGetUserQuery(userId)
Компонент становится наблюдателем конкретного cache entry. Любое обновление данных в кэше автоматически вызывает перерендер всех подписанных компонентов.
RTK Query устраняет необходимость ручного поднятия состояния (lifting state up). Несколько компонентов могут независимо использовать один и тот же endpoint, не зная друг о друге.
Пример поведения:
useGetPostQuery(1)useGetPostQuery(1)При этом система подписок обеспечивает синхронное обновление данных во всех местах использования.
Когда последний компонент, использующий конкретный cache entry, размонтируется, RTK Query не удаляет данные мгновенно. Вместо этого запускается таймер очистки.
Параметр:
keepUnusedDataFor: 60
означает, что данные будут храниться в кэше ещё 60 секунд после исчезновения последнего подписчика.
Это поведение позволяет:
Компонент может инициировать повторную загрузку данных при повторном монтировании через настройки:
refetchOnMountOrArgChangerefetchOnFocusrefetchOnReconnectЭти параметры определяют, будет ли компонент использовать кэш или выполнять повторный запрос даже при наличии актуальных данных.
Особенно важен сценарий:
useGetUserQuery(id, {
refetchOnMountOrArgChange: true
})
В этом случае компонент становится источником актуализации данных при каждом появлении в дереве.
RTK Query строго разделяет данные по аргументам. Даже небольшое отличие приводит к новому cache entry.
useGetUsersQuery({ page: 1 })
useGetUsersQuery({ page: 2 })
Это два независимых состояния кэша.
На уровне компонентов это означает, что pagination, фильтры и сортировки автоматически становятся частью кэш-ключа, если передаются как аргументы endpoint.
На уровне компонентов возможна тонкая оптимизация рендеров с помощью
selectFromResult. Этот механизм позволяет подписываться не
на весь результат запроса, а на его часть.
const { username } = api.useGetUserQuery(id, {
selectFromResult: ({ data }) => ({
username: data?.username
})
})
В этом случае компонент перерисовывается только при изменении
username, даже если остальная часть объекта
data обновилась.
Это особенно важно при:
Компоненты могут находиться в разных состояниях интерфейса, но использовать один и тот же кэш:
RTK Query обеспечивает консистентность данных между всеми контекстами UI, так как кэш не привязан к маршрутам или иерархии компонентов.
Каждое изменение данных в кэше вызывает уведомление подписчиков. Это означает, что компонент не хранит локальную копию результата — он всегда получает данные из централизованного store.
При этом обновление может происходить:
invalidatesTagsupdateQueryDataКомпонент не различает источник обновления, он реагирует только на изменение cache entry.
Компонент может временно отключить подписку через
skip:
useGetUserQuery(id, { skip: !id })
В этом состоянии:
Однако данные, уже находящиеся в кэше, остаются доступными для других компонентов.
При повторном монтировании компонента RTK Query проверяет:
keepUnusedDataForЕсли данные ещё актуальны, компонент мгновенно получает их без сетевого запроса, что создаёт ощущение локального состояния.
Если один и тот же компонент используется несколько раз на странице, каждый экземпляр создаёт собственную подписку, но все они привязаны к одному cache entry при совпадении аргументов.
Это приводит к:
RTK Query не перерисовывает весь компонент при любом изменении кэша. Перерисовка происходит только если изменился результат подписки.
Комбинация с selectFromResult позволяет добиться
максимально узкой реактивности, когда изменение в одном поле не
затрагивает весь UI.
На уровне компонентов аргументы запроса становятся декларативным описанием данных. Компонент не управляет кэшем напрямую — он описывает:
RTK Query затем сам связывает эти параметры с кэшом, подписками и сетевыми запросами, обеспечивая единый источник состояния для всех компонентов, использующих один endpoint.