Redux Toolkit (RTK)
Focus is on why, when, and how, not just syntax.
Table of Contents
- What is Redux Toolkit?
- Why Redux Exists
- Problems with Classic Redux
- Core Principles of Redux Toolkit
- Redux Toolkit Architecture
- configureStore
- createSlice
- Immer
- Action Creators
- Async Logic — createAsyncThunk
- useSelector & useDispatch
- Typed Redux
- Normalized State Shape
- createEntityAdapter
- Selectors
- Redux vs Context
- Redux Toolkit Query
- Common Anti-Patterns
- Folder Structure
- Performance Considerations
- Real Interview Questions
- When NOT to Use Redux
What is Redux Toolkit?
Redux Toolkit is the official, recommended way to write Redux logic.
It solves the three biggest problems of classic Redux:
- Too much boilerplate
- Complex immutable updates
- Difficult async handling
Why Redux Exists (Interview Context)
Redux solves:
- Global state management
- Predictable state updates
- Single source of truth
- Debuggability (time-travel, logs)
React alone is not enough when:
- Many components need shared state
- State transitions are complex
- Business logic grows
Problems with Classic Redux
ACTION → DISPATCH → REDUCER → STORE
Issues:
- Separate action types
- Switch-case reducers
- Manual immutability
- Thunks written separately
RTK abstracts all of this.
Core Principles of Redux Toolkit
- Single global store
- State is immutable (handled internally)
- Reducers must be pure
- Actions describe what happened
- Reducers describe how state changes
Redux Toolkit Architecture
UI
↓ dispatch
Slice Actions
↓
Reducers (Immer)
↓
Store
↓
Selectors
↓
UI
1. configureStore
Why configureStore?
-
Replaces
createStore -
Automatically sets up:
- Redux DevTools
- Thunk middleware
- Better defaults
Example
import { configureStore } from "@reduxjs/toolkit";
import authReducer from "./authSlice";
export const store = configureStore({
reducer: {
auth: authReducer,
},
});
Interview Insight
configureStore enforces best practices by default.
2. createSlice (Most Important Concept)
What is a Slice?
A slice contains:
- Initial state
- Reducers
- Auto-generated action creators
Example
import { createSlice } from "@reduxjs/toolkit";
const authSlice = createSlice({
name: "auth",
initialState: {
user: null,
token: null,
},
reducers: {
loginSuccess(state, action) {
state.user = action.payload.user;
state.token = action.payload.token;
},
logout(state) {
state.user = null;
state.token = null;
},
},
});
export const { loginSuccess, logout } = authSlice.actions;
export default authSlice.reducer;
Key Interview Point
This looks mutable, but RTK uses Immer under the hood.
3. Immer (Hidden Superpower)
What is Immer?
Immer allows you to write mutable-looking code that produces immutable updates.
state.count += 1; // SAFE
Internally:
Draft → Diff → Immutable copy
Interview Question
Q: Why is immutability important? A: Enables change detection, time-travel debugging, and predictable updates.
4. Action Creators (Auto-Generated)
dispatch(loginSuccess(payload));
No need to manually write:
{
type: "LOGIN_SUCCESS", payload;
}
5. Async Logic — createAsyncThunk
Why Thunks?
Redux reducers must be synchronous & pure.
Async logic belongs in thunks.
createAsyncThunk Example
import { createAsyncThunk } from "@reduxjs/toolkit";
export const fetchUser = createAsyncThunk("auth/fetchUser", async (userId) => {
const res = await fetch(`/api/users/${userId}`);
return res.json();
});
Handling Async States in Slice
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = true;
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = false;
state.user = action.payload;
})
.addCase(fetchUser.rejected, (state) => {
state.loading = false;
state.error = true;
});
};
Async Lifecycle
| State | Meaning |
|---|---|
| pending | API started |
| fulfilled | Success |
| rejected | Failure |
6. useSelector & useDispatch
useSelector
const user = useSelector((state) => state.auth.user);
useDispatch
const dispatch = useDispatch();
dispatch(logout());
7. Typed Redux (TypeScript Best Practice)
Infer Store Types
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
Typed Hooks
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector = useSelector<RootState>;
Interview Expectation
Senior engineers always type Redux.
8. Normalized State Shape
Bad (Nested)
users: [{ posts: [{ comments: [] }] }];
Good (Normalized)
users: { byId: {}, allIds: [] }
posts: { byId: {}, allIds: [] }
Why?
- Faster updates
- Simpler reducers
- Less re-renders
9. createEntityAdapter
Used for normalized collections.
const usersAdapter = createEntityAdapter();
Gives:
- addOne
- updateOne
- removeOne
- selectors
10. Selectors (Performance Critical)
What are Selectors?
Functions that extract data from store.
const selectUser = (state) => state.auth.user;
Memoized Selectors
import { createSelector } from "@reduxjs/toolkit";
const selectFiltered = createSelector(
[selectItems, selectFilter],
(items, filter) => items.filter(...)
);
11. Redux vs Context (Interview Favorite)
| Redux | Context |
|---|---|
| Predictable | Simple |
| Debuggable | Lightweight |
| Scales well | Not for heavy logic |
| Middleware support | No middleware |
Rule of Thumb
- Context → Theme, auth flag
- Redux → Business state, async-heavy logic
12. Redux Toolkit Query (RTK Query)
What is RTK Query?
A data-fetching & caching layer built into RTK.
Why Use It?
- Automatic caching
- Auto refetching
- Loading & error handling
- No manual thunks
Example
const api = createApi({
reducerPath: "api",
baseQuery: fetchBaseQuery({ baseUrl: "/api" }),
endpoints: (builder) => ({
getUsers: builder.query<User[], void>({
query: () => "/users",
}),
}),
});
const { data, isLoading } = useGetUsersQuery();
13. Common Anti-Patterns (Senior Red Flags)
- Putting derived data in Redux
- Overusing global state
- Huge slices
- Mixing UI state with business state
- API calls inside components
14. Folder Structure (Production-Ready)
store/
index.ts
features/
auth/
authSlice.ts
authApi.ts
selectors.ts
15. Performance Considerations
- Split slices logically
- Memoize selectors
- Avoid deep nested state
- Prefer RTK Query for APIs
16. Real Interview Questions
Q: Why Redux Toolkit over Redux?
- Less boilerplate
- Built-in best practices
- Safer immutability
- Better DX
Q: Where should async logic live?
- Thunks / RTK Query
Q: Can Redux replace backend state?
- No, Redux is a client cache, not a database
17. When NOT to Use Redux
- Small apps
- Local UI state
- Single-page forms
- Simple data flow