In Part 3, we looked at how Tanstack Query revolutionized caching by moving server state out of our global state managers. But what if you work on a massive enterprise application that is already deeply invested in Redux?
Do you have to rip out Redux to get good caching? Absolutely not.
Welcome to Part 4. Let's talk about the tool that made Redux fun again: RTK Query.
Introduction
If you spend enough time on tech Twitter (X), you will inevitably hear people saying things like "Redux is dead."
This is not true as redux is powering some of the largest, most complex applications on the internet. However, the way we write Redux has fundamentally changed with the introduction of Redux-Toolkit (RTK)
In the old days, fetching a list of users in Redux required a mountain of boilerplate:
- Write an
ActionforFETCH_USERS_REQUEST. - Write an
ActionforFETCH_USERS_SUCCESS. - Write an
ActionforFETCH_USERS_FAILURE. - Write a massive
Thunkto make the actual API call. - Write a
Reducerto handle theisLoading,data, anderrorstates.
It was exhausting. And worst of all? It didn't even cache the data properly. If you navigated away and came back, your Thunk would just fetch it all over again.
In Part 4 of our series, we are looking at RTK Query (Redux Toolkit Query) which was primarily built for data fetching and caching solution integrated directly into Redux.
The Architecture: Centralized vs. Decentralized
In Part 3, we looked at React Query. React Query is largely decentralized. You call useQuery wherever you need it, and it magically syncs up based on a queryKey (like ['users']).
RTK Query takes a different, highly structured approach. It uses the API Slice pattern.
Instead of writing fetch calls scattered across your components, you define your entire API in one central location.
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const apiSlice = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => '/users',
}),
updateUser: builder.mutation({
query: (user) => ({
url: `/users/${user.id}`,
method: 'PUT',
body: user,
}),
}),
}),
});
// RTK Query auto-generates custom React hooks for you
export const { useGetUsersQuery, useUpdateUserMutation } = apiSlice;
With just that block of code, RTK Query replaces hundreds of lines of legacy Redux boilerplate. It automatically handles the fetching, the isLoading state, the deduplication, and the caching.
Solving the Hardest Problem using "Tags"
In Part 1, we established that cache invalidation (knowing when to throw old data away) is one of the hardest problems in computer science.
RTK Query solves this using an incredibly elegant system called Automated Tag-based Invalidation.
Think of a Tag like a sticky note you put on a piece of data.
providesTags(Applying the label):
When you fetch the list of users, you tell RTK Query to tag that cached data with['User'].invalidatesTags(Tearing off the label):
When you run a mutation to update a specific user, you tell RTK Query to invalidate the['User']tag.
// Inside your endpoints builder:
getUsers: builder.query({
query: () => '/users',
providesTags: ['User'], // Label this data as 'User'
}),
updateUser: builder.mutation({
query: (user) => ({ url: `/users/${user.id}`, method: 'PUT' }),
invalidatesTags: ['User'], // Throw away any cache labeled 'User'
}),
The moment updateUser succeeds, RTK Query automatically sees that the ['User'] tag was invalidated. It instantly looks at your app, finds any component currently displaying the getUsers query, and forces a background refetch.
Your UI updates automatically. No manual dispatching. No messy states.
React Query vs. RTK Query: Which should you choose?
Since both tools solve the same problem (Server State caching), which one should you use?
- Choose React Query: If you are starting a new project, you don't need a massive global client-state store, or you prefer a lightweight, flexible, decentralized approach.
-
Choose RTK Query: If you are already using Redux Toolkit. Adding React Query to an existing Redux app means you now have two separate stores to manage and debug. RTK Query drops right into your existing
Redux DevTools, keeping your architecture clean and unified.
Whatβs Next?
Up to this point, we have focused entirely on caching data inside the user's browser (Client-Side Caching). But the frontend world is shifting.
In Part 5, the grand finale of this series, we are crossing the chasm into Server-Side Caching. We will look at Next.js (using App Router) and how it aggressively caches not just data, but entire HTML pages and React Server Components.
Prepare to have your mental model challenged one last time. See you in Part 5.
Top comments (0)