Redux
A predictable state container: one store, state changed only by dispatching actions, reducers that are pure functions of (state, action). Everything Redux does follows from those three rules.
Write new Redux with Redux Toolkit. Hand-written action-type constants, action creators and switch reducers are the pattern RTK replaced, and the official documentation says so. The older style is worth recognising because you will read it, not because you should write it.
Files
| File | Covers |
|---|---|
| redux.md | the three principles, the classic hand-written style you will meet in legacy code |
| redux-toolkit.md | createSlice, configureStore, Immer, createAsyncThunk - the current way |
| rtk_query.md | caching, deduplication and invalidation for server data |
| dispatch_function.md | what dispatch does, useDispatch, why actions must be serialisable |
| selectors_in_redux.md | encapsulating state shape, why useSelector re-renders too often |
| reselect_in_redux.md | memoized derived state, the cache-size-1 problem and what Reselect 5 changed |
| Redux_middleware.md | the store => next => action chain, where side effects live |
| synchronous_vs_asynchronous_middleware.md | how thunks work, ordering, cancelling in-flight work |
| mapStateToProps.md | legacy connect API - for reading existing code |
| mapDispatchToProps.md | legacy connect API - for reading existing code |
The shape of modern Redux
import { createSlice, configureStore } from '@reduxjs/toolkit';
const todos = createSlice({
name: 'todos',
initialState: { items: [], filter: 'all' },
reducers: {
added(state, action) { state.items.push(action.payload); }, // Immer draft
filtered(state, action) { state.filter = action.payload; },
},
});
export const { added, filtered } = todos.actions;
export const store = configureStore({ reducer: { todos: todos.reducer } });
createSlice generates the action types, action creators and reducer from one object. The apparently mutating code is Immer running against a draft proxy and producing an immutable next state — it only works inside createSlice/createReducer, and the same code elsewhere really does mutate.
In components, use the hooks: useSelector to read, useDispatch to dispatch. connect with mapStateToProps/mapDispatchToProps is the legacy API kept for class components.
Does your project need Redux?
Often not, and being able to say so is the stronger answer.
| State | Where it belongs |
|---|---|
| Server data | a query library — RTK Query, TanStack Query. It owns caching, deduplication, revalidation and invalidation. |
| Form state | a form library, or local component state |
| UI state used by one subtree | component state, or context |
| Genuinely global client state with complex transitions | Redux, or a smaller store like Zustand |
Redux earns its place when you need a large shared state graph, strict traceability of every change, or time-travel debugging on a big team. It does not earn its place as a hand-written cache for fetched data — see ../server_vs_client_state.md and ../choosing_a_state_library.md.
Interview angle
- “What are Redux’s three principles?” - single source of truth, state is read-only and changed only by dispatching actions, and changes are made by pure reducers. Everything else follows.
- “Why must reducers be pure?” - predictability, replayability and time-travel debugging. A reducer that fetches, mutates its argument or reads the clock makes the same action produce different state, breaking all three.
- “How can RTK reducers look like they mutate?” - Immer. The reducer runs against a draft proxy and RTK derives the immutable next state from the recorded changes.
- “Why does my component re-render on every store change?” -
useSelectorcompares with===, so a selector returning a new object or the result of.map/.filterfails the check every time. Select primitives, passshallowEqual, or memoize with Reselect. - “Would you choose Redux for a new project?” - only if the state is genuinely global, complex and needs auditability. Server data goes in a query library and modest client state in component state or Zustand. Reaching for Redux by default in 2026 is the answer that invites pushback.