How does RTK Query cache data?
1. What RTK Query does overall
RTK Query is a layer on top of Redux Toolkit that:
- makes network requests (fetch/axios, etc.);
- caches responses;
- updates the UI from the cache without repeated requests;
- automatically refetches data when needed.
The idea:
React components don't know about requests directly - they just subscribe to "data", and RTK Query itself decides whether to take it from the cache or from the server.
2. How the cache is structured internally
RTK Query stores cached data in the Redux store inside its own "slice", created via createApi().
const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
endpoints: builder => ({
getUser: builder.query<User, string>({
query: (id) => `/user/${id}`,
}),
}),
});After calling:
const { data } = useGetUserQuery('42');the Redux store gets a structure roughly like this:
state.api = {
queries: {
'getUser("42")': {
status: 'fulfilled',
data: { id: 42, name: 'Tim' },
fulfilledTimeStamp: 1718256635100,
},
},
mutations: {},
provided: { users: { '42': ['getUser("42")'] } },
}That is, RTK Query stores:
- the query key (
endpointName + arguments); - the resulting data (
data); - the cache time;
- references to components subscribed to that data.
3. Key concept: Query Cache Key
Each query forms a unique cache key, based on:
- the endpoint name (
getUser), - the arguments (
'42').
Example:
const cacheKey = 'getUser("42")'React components calling useGetUserQuery('42') subscribe to the same key.
If data for that key already exists, RTK Query takes it from the cache instead of fetching again.
4. When RTK Query uses the cache vs. makes a new request
| Scenario | What RTK Query does |
|---|---|
Another component calls useGetUserQuery('42') | Uses the cache (a single source of truth) |
Fewer than keepUnusedDataFor seconds have passed since the last component unmounted | Still keeps the data in the cache |
More than keepUnusedDataFor seconds have passed | Removes the cache |
refetch() is called | Makes a new request, updates the cache |
| An "invalidation" happens via a tag | Removes the cache and makes a new request |
5. keepUnusedDataFor: cache lifetime
By default, RTK Query keeps data in the cache for 60 seconds after the last subscribed component unmounts.
builder.query({
query: () => '/posts',
keepUnusedDataFor: 120, // keep data for 2 minutes
});If another component requests /posts again within those 120 seconds,
RTK Query takes the data from the cache, without a network request.
6. Tag-based cache and automatic invalidation
RTK Query supports tags to manage the cache at the entity level (users, posts, etc.).
Example:
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 }],
}),
}),
});What happens:
getUser(42)caches data under the tag{type: 'User', id: 42}.- When
updateUser({id: 42, ...})is called, RTK Query invalidates the tag. - All cached queries that provide that tag (
providesTags) become invalid. - RTK Query automatically refetches the data.
7. Behavior with multiple components
function Profile() {
const { data } = useGetUserQuery('42');
return <div>{data?.name}</div>;
}
function Sidebar() {
const { data } = useGetUserQuery('42');
return <div>{data?.name}</div>;
}Both components use the same cache key, so there's just one request to the server.
- The first component triggers the fetch.
- The second gets the data from the cache immediately, without a request.
- If one of them unmounts, the cache still lives until
keepUnusedDataForexpires.
8. How RTK Query tracks freshness
Each cached query has metadata:
fulfilledTimeStamp(time of the last update),isFetching,isSuccess,isError,isUninitialized.
You can manually call:
refetch(); // updates the cacheor use:
pollingInterval: 10000 // automatically update every 10 seconds9. Cache at the Redux store level
If you look into Redux DevTools, you see something like this:
state.api.queries["getUser(42)"] = {
status: "fulfilled",
data: { id: 42, name: "Tim" },
fulfilledTimeStamp: 1718256635100,
originalArgs: 42,
requestId: "getUser-42-1",
}Each cache entry lives independently and has its own TTL (time-to-live).
10. Summary: how RTK Query caches data
| Mechanism | What it does |
|---|---|
Cache by key (endpoint + arguments) | A single request per unique set of arguments |
TTL via keepUnusedDataFor | Keeps data after unmounting |
Tags (providesTags / invalidatesTags) | Granular updates on mutations |
refetch / pollingInterval | Refreshing stale data |
| Redux store | All data is centrally cached in state |
| Automatic reuse | Components reuse ready cache entries |
Example of a full caching cycle
-
A component calls
useGetUserQuery(42)-> RTK Query makes a request -> stores it in the store -> The component receivesdata -
A second component calls
useGetUserQuery(42)-> RTK Query sees the data already exists -> Returns it immediately (0ms) -
The component unmounts -> RTK Query waits
keepUnusedDataForseconds -
If the component mounts again within that time -> data is taken from the cache, without a fetch
-
If a mutation with
invalidatesTagsis called -> the corresponding cache entries are removed -> RTK Query does a refetch.
Summary
RTK Query caches data by a unique query key, stores it in the Redux store, automatically invalidates it on changes, and reuses it across components.
Cheat sheet:
- All data lives in the Redux store, a single source of truth.
keepUnusedDataForcontrols the cache lifetime.providesTags/invalidatesTagscontrol automatic refetching.- Repeated calls to
useXxxQuery()with the same arguments don't refetch the data.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.