Як RTK Query кешує дані?
1. Що робить RTK Query в цілому
RTK Query - це шар над Redux Toolkit, який:
- робить мережеві запити (fetch/axios і т.п.);
- кешує відповіді;
- оновлює UI з кешу без повторних запитів;
- автоматично рефетчить дані за потреби.
Ідея:
React-компоненти не знають про запити напряму - вони просто підписані на "дані", а RTK Query сама вирішує, брати їх з кешу чи з сервера.
2. Як кеш влаштований всередині
RTK Query зберігає кешовані дані в Redux store всередині свого "slice", створеного через createApi().
const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
endpoints: builder => ({
getUser: builder.query<User, string>({
query: (id) => `/user/${id}`,
}),
}),
});Після виклику:
const { data } = useGetUserQuery('42');у Redux-сховищі з'являється структура приблизно такого вигляду:
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').
Приклад:
const cacheKey = 'getUser("42")'React-компоненти, що викликають useGetUserQuery('42'), підписуються на один і той самий ключ.
Якщо дані для цього ключа вже є - RTK Query візьме їх з кешу, а не буде заново фетчити.
4. Коли RTK Query використовує кеш, а коли робить новий запит
| Сценарій | Що робить RTK Query |
|---|---|
Інший компонент викликає useGetUserQuery('42') | Використовує кеш (одне джерело істини) |
Пройшло менше keepUnusedDataFor секунд після розмонтування останнього компонента | Все ще зберігає дані в кеші |
Пройшло більше keepUnusedDataFor секунд | Видаляє кеш |
Викликано refetch() | Робить новий запит, оновлює кеш |
| Сталася "інвалідація" через тег | Видаляє кеш і робить новий запит |
5. keepUnusedDataFor: час життя кешу
Типово RTK Query тримає дані в кеші 60 секунд після того, як останній підписаний компонент розмонтувався.
builder.query({
query: () => '/posts',
keepUnusedDataFor: 120, // зберігати дані 2 хвилини
});Якщо інший компонент протягом цих 120 секунд знову запросить /posts,
RTK Query візьме дані з кешу, без мережевого запиту.
6. Кеш на основі тегів і автоматична інвалідація
RTK Query підтримує теги, щоб керувати кешем на рівні сутностей (users, posts і т.п.).
Приклад:
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 }],
}),
}),
});Що відбувається:
getUser(42)кешує дані під тегом{type: 'User', id: 42}.- Коли викликається
updateUser({id: 42, ...}), RTK Query інвалідує тег. - Усі кешовані запити, які цей тег "надають" (
providesTags), стають недійсними. - RTK Query автоматично перезапитує дані.
7. Поведінка з кількома компонентами
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.
Можна вручну викликати:
refetch(); // оновить кешабо використовувати:
pollingInterval: 10000 // автоматично оновлювати кожні 10 секунд9. Кеш на рівні Redux store
Якщо заглянути в Redux DevTools, видно приблизно так:
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 |
| Автоматичне повторне використання | Компоненти повторно використовують готові кеш-записи |
Приклад повного циклу кешування
-
Компонент викликає
useGetUserQuery(42)-> RTK Query робить запит -> зберігає в store -> Компонент отримуєdata -
Другий компонент викликає
useGetUserQuery(42)-> RTK Query бачить, що дані вже є -> Повертає їх одразу (0ms) -
Компонент розмонтується -> RTK Query чекає
keepUnusedDataForсекунд -
Якщо за цей час компонент знову змонтується -> дані беруться з кешу, без fetch
-
Якщо викликається мутація з
invalidatesTags-> відповідні записи кешу видаляються -> RTK Query робить refetch.
Підсумок
RTK Query кешує дані за унікальним ключем запиту, зберігає їх у Redux store, автоматично інвалідує при змінах і повторно використовує між компонентами.
Пам'ятка:
- Усі дані живуть у Redux store -> одне джерело істини.
keepUnusedDataForкерує часом життя кешу.providesTags/invalidatesTagsкерують автоматичним refetch.- Повторні виклики
useXxxQuery()з тими самими аргументами не фетчать дані повторно.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.