Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Як RTK Query кешує дані?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**RTK Query** - це шар над Redux Toolkit, який виконує мережеві запити, кешує відповіді в Redux store і оновлює UI з кешу без повторних запитів. React-компоненти не знають про запити напряму - вони підписані на "дані", а RTK Query сама вирішує, брати їх з кешу чи з сервера. **Ключове:** RTK Query кешує дані за унікальним ключем запиту (`endpoint + аргументи`), зберігає їх у Redux store і автоматично інвалідує через `providesTags` / `invalidatesTags`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## 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()` з тими самими аргументами не фетчать дані повторно.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.