Understand Redux,
one step at a time.
Interact with any control below. Every dispatched action walks through the Redux cycle in the panel on the right — step by step, at your pace.
Controls — dispatch actions here
counterSlice
state.counter.value
userSlice
state.user
todosSlice
state.todos.items[]
- empty — add one above
cartSlice
state.cart.items
- 📘
Redux Handbook
$29
- 🧰
RTK Starter Kit
$19
- 🔷
TypeScript Guide
$24
- ⚛️
React Patterns
$34
createAsyncThunk
pending → fulfilled | rejected
Watch the action log → async/fetchUser/pending fires instantly, then fulfilled or rejected after ~1.4s.
Action Composer
Manually build and dispatch any action to the store
Quick actions
Subscription Map
Subscription Map
Which component re-renders when each slice changes
Components
Slices
Only the component subscribed to the changed slice re-renders. Others are skipped by React-Redux equality check.
20 Examples
basic → advancedSimplest form — the reducer knows what to do without any extra data.
Same zero-payload pattern, opposite direction.
Reducer replaces the current value with the hardcoded initialState.
Sets every field back to its initial value in one action.
A string becomes the payload. Reducer constructs a new object and pushes it.
No payload needed. Reducer replaces the array with a filtered copy.
Payload is a union string. Controls which items the UI renders.
Core Concepts
Store
Single source of truth. configureStore() composes your slices into one state tree.
Slice
RTK bundle of initialState + reducers + action creators for one feature.
Reducer
(state, action) → newState. Pure function. Immer lets you write mutations safely.
Action
Plain object: { type, payload? }. Describes what happened, not how to change state.
dispatch()
Sends an action through middleware → reducers → subscribers. Returns the action.
Selector
fn(state) → value. Component re-renders only when the selected value changes.
Middleware
Intercepts every dispatch. Runs between dispatch() and reducers. Powers thunks, logging.
createAsyncThunk
Wraps async work. Auto-dispatches /pending, /fulfilled, /rejected lifecycle actions.
extraReducers
Lets a slice react to actions from other slices or async thunks via builder.addCase().
How Action Names Are Generated
Redux Toolkit action type formula
type = sliceName/reducerName (or thunkPrefix/lifecycle)
createSlice({ name: 'counter' })
dispatch(increment())
counter/increment
Increment comes from the reducer key increment in counterSlice.
createSlice({ name: 'todos' })
dispatch(addTodo('Learn RTK'))
todos/addTodo
Payload carries the todo text, but payload is not part of the action type string.
createSlice({ name: 'cart' })
dispatch(setQuantity({ id: 2, qty: 3 }))
cart/setQuantity
Object payload still maps to one stable type, so reducers can match quickly.
createSlice({ name: 'user' })
dispatch(login({ name, email }))
user/login
The slice name user namespaces all user actions to avoid collisions.
createAsyncThunk('async/fetchUser', ...)
dispatch(fetchUser({ id: 1, fail: false }))
async/fetchUser/pending then fulfilled|rejected
Thunk lifecycle actions are generated automatically by Redux Toolkit.
createAction('@@timeTravel/load')
dispatch(loadSnapshot(snapshot))
@@timeTravel/load
Manual action type string: useful for cross-slice global actions.
Where Each Action Goes
1. Component dispatches
Buttons and handlers call dispatch(actionCreator(payload)).
2. Middleware chain runs
Thunk/default middleware runs first, then flowLoggerMiddleware records prevState and nextState.
3. Root reducer routes by type
configureStore reducer map sends counter/* to counterSlice, user/* to userSlice, and so on.
4. Matching case reducer updates state
createSlice reducer key matches action.type and Immer applies immutable updates.
5. Subscribers/selectors re-check
useAppSelector compares selected values; only affected components re-render.
6. FlowVisualizer displays the trip
flowEvents receives action + state snapshots so you can see exactly what changed.