Skip to main content

Як RTK Query кешує дані?

1. Що робить RTK Query в цілому

RTK Query - це шар над Redux Toolkit, який:

  • робить мережеві запити (fetch/axios і т.п.);
  • кешує відповіді;
  • оновлює UI з кешу без повторних запитів;
  • автоматично рефетчить дані за потреби.

Ідея:

React-компоненти не знають про запити напряму - вони просто підписані на "дані", а RTK Query сама вирішує, брати їх з кешу чи з сервера.


2. Як кеш влаштований всередині

RTK Query зберігає кешовані дані в Redux store всередині свого "slice", створеного через createApi().

javascript
const api = createApi({ reducerPath: 'api', baseQuery: fetchBaseQuery({ baseUrl: '/api' }), endpoints: builder => ({ getUser: builder.query<User, string>({ query: (id) => `/user/${id}`, }), }), });

Після виклику:

javascript
const { data } = useGetUserQuery('42');

у Redux-сховищі з'являється структура приблизно такого вигляду:

javascript
state.api = { queries: { 'getUser("42")': { status: 'fulfilled', data: { id: 42, name: 'Tim' }, fulfilledTimeStamp: 1718256635100, }, }, mutations: {}, provided: { users: { '42': ['getUser("42")'] } }, }

Тобто RTK Query зберігає:

  • ключ запиту (endpointName + аргументи);
  • результат даних (data);
  • час кешування;
  • посилання на компоненти, які підписані на ці дані.

3. Ключова концепція - Query Cache Key

Кожен запит формує унікальний cache key, заснований на:

  • імені ендпоінту (getUser),
  • аргументах ('42').

Приклад:

javascript
const cacheKey = 'getUser("42")'

React-компоненти, що викликають useGetUserQuery('42'), підписуються на один і той самий ключ. Якщо дані для цього ключа вже є - RTK Query візьме їх з кешу, а не буде заново фетчити.


4. Коли RTK Query використовує кеш, а коли робить новий запит

СценарійЩо робить RTK Query
Інший компонент викликає useGetUserQuery('42')Використовує кеш (одне джерело істини)
Пройшло менше keepUnusedDataFor секунд після розмонтування останнього компонентаВсе ще зберігає дані в кеші
Пройшло більше keepUnusedDataFor секундВидаляє кеш
Викликано refetch()Робить новий запит, оновлює кеш
Сталася "інвалідація" через тегВидаляє кеш і робить новий запит

5. keepUnusedDataFor: час життя кешу

Типово RTK Query тримає дані в кеші 60 секунд після того, як останній підписаний компонент розмонтувався.

javascript
builder.query({ query: () => '/posts', keepUnusedDataFor: 120, // зберігати дані 2 хвилини });

Якщо інший компонент протягом цих 120 секунд знову запросить /posts, RTK Query візьме дані з кешу, без мережевого запиту.


6. Кеш на основі тегів і автоматична інвалідація

RTK Query підтримує теги, щоб керувати кешем на рівні сутностей (users, posts і т.п.).

Приклад:

javascript
const api = createApi({ baseQuery: fetchBaseQuery({ baseUrl: '/api' }), tagTypes: ['User'], endpoints: builder => ({ getUser: builder.query<User, number>({ query: id => `/user/${id}`, providesTags: (result, error, id) => [{ type: 'User', id }], }), updateUser: builder.mutation<void, User>({ query: user => ({ url: `/user/${user.id}`, method: 'PUT', body: user, }), invalidatesTags: (result, error, user) => [{ type: 'User', id: user.id }], }), }), });

Що відбувається:

  1. getUser(42) кешує дані під тегом {type: 'User', id: 42}.
  2. Коли викликається updateUser({id: 42, ...}), RTK Query інвалідує тег.
  3. Усі кешовані запити, які цей тег "надають" (providesTags), стають недійсними.
  4. RTK Query автоматично перезапитує дані.

7. Поведінка з кількома компонентами

javascript
function Profile() { const { data } = useGetUserQuery('42'); return <div>{data?.name}</div>; } function Sidebar() { const { data } = useGetUserQuery('42'); return <div>{data?.name}</div>; }

Обидва компоненти використовують один і той самий cache key -> один запит до сервера.

  • Перший компонент ініціює fetch.
  • Другий отримує дані з кешу одразу, без запиту.
  • Якщо один з них розмонтується - кеш все ще живе до закінчення keepUnusedDataFor.

8. Як RTK Query відстежує актуальність

Кожен кешований запит має метадані:

  • fulfilledTimeStamp (час останнього оновлення),
  • isFetching,
  • isSuccess,
  • isError,
  • isUninitialized.

Можна вручну викликати:

javascript
refetch(); // оновить кеш

або використовувати:

javascript
pollingInterval: 10000 // автоматично оновлювати кожні 10 секунд

9. Кеш на рівні Redux store

Якщо заглянути в Redux DevTools, видно приблизно так:

javascript
state.api.queries["getUser(42)"] = { status: "fulfilled", data: { id: 42, name: "Tim" }, fulfilledTimeStamp: 1718256635100, originalArgs: 42, requestId: "getUser-42-1", }

Кожен cache entry живе незалежно і має власний TTL (time-to-live).


10. Підсумок - як RTK Query кешує дані

МеханізмЩо робить
Кеш за ключем (endpoint + аргументи)Один запит на унікальні аргументи
TTL через keepUnusedDataForЗберігає дані після розмонтування
Теги (providesTags / invalidatesTags)Гранулярне оновлення при мутаціях
refetch / pollingIntervalОновлення застарілих даних
Redux storeУсі дані централізовано кешуються в state
Автоматичне повторне використанняКомпоненти повторно використовують готові кеш-записи

Приклад повного циклу кешування

  1. Компонент викликає useGetUserQuery(42) -> RTK Query робить запит -> зберігає в store -> Компонент отримує data

  2. Другий компонент викликає useGetUserQuery(42) -> RTK Query бачить, що дані вже є -> Повертає їх одразу (0ms)

  3. Компонент розмонтується -> RTK Query чекає keepUnusedDataFor секунд

  4. Якщо за цей час компонент знову змонтується -> дані беруться з кешу, без fetch

  5. Якщо викликається мутація з invalidatesTags -> відповідні записи кешу видаляються -> RTK Query робить refetch.


Підсумок

RTK Query кешує дані за унікальним ключем запиту, зберігає їх у Redux store, автоматично інвалідує при змінах і повторно використовує між компонентами.

Пам'ятка:

  • Усі дані живуть у Redux store -> одне джерело істини.
  • keepUnusedDataFor керує часом життя кешу.
  • providesTags / invalidatesTags керують автоматичним refetch.
  • Повторні виклики useXxxQuery() з тими самими аргументами не фетчать дані повторно.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.