State Management
Redux Toolkit
Master Redux Toolkit (RTK), covering configureStore, slices creation, reactive useSelector selections, action dispatching, and async Thunk actions.
1. Learning Objectives
In this lesson, you will master Redux Toolkit (RTK) as a robust global state management solution. By the end of this topic, you will be able to:
- Configure a centralized store using the
configureStoreAPI. - Create slice modules using
createSlice, utilizing Immer to write safe state mutations. - Read store state reactively using the selector pattern (
useSelector). - Dispatch actions to trigger reducers using
useDispatch. - Manage asynchronous operations and side effects using
createAsyncThunk.
2. Overview
Redux is a pattern and library for managing global application state. **Redux Toolkit (RTK)** is the official, recommended modern standard for writing Redux code. It eliminates the boilerplate code associated with traditional Redux by consolidating store configurations, action creations, and reducers.
3. Why This Topic Matters
Redux Toolkit provides a scalable, predictable state management pattern for large applications:
- Unidirectional Data Flow: Store updates follow a predictable path (action -> reducer -> store update -> UI render), making debugging and tracking state changes straightforward.
- Immer Integration: RTK includes Immer under the hood. This allows you to write code that looks like it directly mutates state (e.g.
state.items.push(item)) while Immer handles translating it into a safe, immutable update. - Middleware and DevTools: RTK configures the Redux DevTools extension and essential middleware (like Redux Thunk for async calls) out of the box.
4. Real-World Analogy
Think of Redux Toolkit like **a banking system**:
- The Store (The Bank Vault): The centralized location where all money (state) is securely stored. You cannot walk in and modify the cash directly.
- Actions (The Deposit/Withdrawal Slip): Form documents describing exactly what transaction you want to make (e.g. `{ type: 'deposit', amount: 100 }`).
- Reducers (The Tellers): The only people authorized to modify the vault's contents. They read your slip (the action), verify the rules, update the ledger, and return the new balance.
- Selectors (The Mobile App): Your personal screen to view your account balance reactively without seeing other users' balances.
5. Core Concepts
Below is a comparison of traditional Redux patterns and Redux Toolkit:
| Property | Traditional Redux Pattern | Redux Toolkit (RTK) Pattern |
|---|---|---|
| Store Setup | Manual configuration combining reducers, middlewares, and devtools. | Simplified via configureStore(). DevTools and Thunk are built-in. |
| Action Creators | Written manually as separate functions returning action objects. | Generated automatically inside createSlice(). |
| Immutability rules | Requires manual object copying (spread operators, deep clones). | Immer handles copies automatically, allowing direct mutations in code. |
| Async Operations | Requires installing external packages like redux-thunk or redux-saga. |
Built-in via createAsyncThunk(). |
6. Syntax & API Reference
This example shows how to configure a slice and set up the store:
7. Visual Diagram
This diagram displays the unidirectional flow inside the Redux architecture:
8. Live Example — Full Working Code
Below is a complete HTML page demonstrating a store setup with React Redux integration:
What just happened? The application is wrapped in the React Redux Provider component, passing the global store to the entire tree. Clicking "Add" dispatches an auto-generated addItem action. The list slice reducer processes this action, updating the items array. Components subscribed to this state via useSelector update their views automatically.
9. Interactive Playground
Try It Yourself Challenges:
- Add a new reducer named
clearAllinsidelistSliceto clear all tasks at once, and add a button in the UI to trigger it. - Modify the task items to store objects with `id`, `text`, and `completed` fields, updating the UI check off toggles accordingly.
10. Common Mistakes
| Mistake | Why it happens | Wrong | Correct |
|---|---|---|---|
| Mutating state outside slice reducers | Immer only runs inside slice reducers created by createSlice(). Directly mutating state values outside of slice reducers (like inside components or selector functions) will cause bugs because React will not detect the state change. |
const items = useSelector(s => s.items); (mutating selector array) |
Treat state as read-only inside components. Only update state values by dispatching actions to slice reducers. |
| Writing non-serializable data to the store | Redux stores are designed to contain only serializable data (plain JavaScript objects, arrays, and primitives). Storing non-serializable objects (like Promises, Functions, or Class instances) breaks devtools debugging and state persistence. | dispatch(saveConnection(new WebSocket())) |
Only write serializable values to the store. Store non-serializable connections in refs or contexts. |
11. Best Practices
- Keep Reducers Pure: Do not make API calls, trigger side effects, or generate random IDs inside slice reducers. Keep reducers pure and deterministic.
- Only Store Serializable Data: Restrict store data to serializable plain JavaScript objects and arrays.
- Use Slices: Organize your store logic into slices using `createSlice` to bundle state, actions, and reducers in single files.
- Use createAsyncThunk for API calls: Always use `createAsyncThunk` or RTK Query for fetching data and managing async state transitions.
12. Browser Compatibility/Requirements
Redux Toolkit is supported in all modern browsers. It requires ES6 compatibility (for Immer proxies).
13. Interview Questions
Q1: How does Redux Toolkit allow us to write code that looks like it mutates state directly, without violating Redux's immutability rules?
Answer: Redux Toolkit uses **Immer** under the hood when processing state updates inside `createSlice`. Immer uses JavaScript proxies to track changes made to a draft state object, and automatically translates these direct mutations into a safe, immutable update, returning a new state reference without manual object copying.
Q2: What is an Async Thunk in Redux, and how do you configure side-effects inside slice reducers?
Answer: A Thunk is a function that wraps an operation to delay its execution. In RTK, `createAsyncThunk` is used to declare async workflows (like API fetch requests). You handle the lifecycle states of the async call (pending, fulfilled, rejected) inside the slice's `extraReducers` block using builder cases. This separates side-effects from the pure state update logic of standard slice reducers.
14. Debugging Exercise
Identify the bug in this async thunk declaration that crashes the store during API calls:
Diagnosis: The thunk returns the raw browser Response object, which is non-serializable. This violates Redux rules and throws an error in the devtools middleware.
Fix: Parse the response body as JSON to return serializable data before resolving the thunk:
15. Practice Exercises
Exercise 1: Build a global settings slice
Build a settings module slice using createSlice. Store states for theme configuration and localization language. Connect the slice to the store, and build components to read and update settings using `useSelector` and `useDispatch`.
16. Scenario-Based Challenge
The Real-time Collaborative Board Store Layout:
You are designing a collaborative whiteboard application where multiple users edit canvas cards simultaneously. Network updates dispatch event actions to update local stores. Describe how you would configure the store and design selectors to prevent the entire canvas grid from re-rendering when a single card coordinate changes.
17. Quick Quiz
Q1: Which library does Redux Toolkit use under the hood to allow safe state mutation syntax in reducers?
A) Redux Thunk
B) Immer
C) Axios
Answer: B — Immer is the proxy library RTK integrates to compile direct draft mutations into safe, immutable state updates.
18. Summary & Key Takeaways
- Redux Toolkit is the recommended standard for writing Redux state management code.
- Create slices using
createSliceto automatically generate action creators and reducers. - Use
useSelectorto read store state anduseDispatchto trigger state updates. - RTK uses Immer under the hood, allowing you to write direct state mutations safely inside slices.
- Manage async calls and side-effects using
createAsyncThunk.
19. Cheat Sheet
| RTK API Element | Purpose |
|---|---|
configureStore({ reducer: { ... } }) |
Configures and creates a centralized Redux store. |
createSlice({ name, initialState, reducers }) |
Defines state, actions, and reducers in a single slice module. |
useSelector(state => state.value) |
Subscribes to and reads values from the store state. |
useDispatch() |
Gets the dispatch function to trigger actions in the store. |