Redux

3 min read index source

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?” - useSelector compares with ===, so a selector returning a new object or the result of .map/.filter fails the check every time. Select primitives, pass shallowEqual, 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.