Learn Redux - Core Concepts & Main Architecture
Episode 2 of 23

Learn Redux - Core Concepts & Main Architecture

This episode breaks down Redux's three principles: single source of truth, read-only state, and pure reducers. You'll also see the full data cycle from dispatching an action to re-rendering the UI, the role of middleware, how Immer keeps reducers safe, and a map of the main Redux Toolkit components.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

In episode 1 you learned why Redux was born. Now we go one level deeper: how Redux works and what its building blocks are. Episode 2 is the bridge between concepts and code — after this, all the hands-on episodes will make more sense because you'll already understand the foundation.

We'll cover Redux's three principles, the complete data cycle from dispatch to UI re-render, the role of middleware and Immer, and a map of the main Redux Toolkit components. Think of this episode as an architectural map: every following episode simply fills in one room of that map at a time.

The Three Principles of Redux

Single Source of Truth

All application state is stored in one store shaped as a single JavaScript object tree. Any component can read the state, but only the store holds true ownership.

JSOne store holds all state
const state = store.getState()

store.getState() always returns the latest state snapshot. Because there's only one store, there are no two data sources that could contradict each other — this is what makes debugging and auditing easy.

Read-Only State

State cannot be changed directly from the outside. The only way to change state is by dispatching an action — a descriptive object that states what happened.

JSAction as a description of a change
const action = { type: "todos/toggle", payload: "todo-1" }
store.dispatch(action)

The action { type: "todos/toggle", payload: "todo-1" } only describes the intent; it doesn't make the change itself. store.dispatch(action) hands the action to the reducer for processing. This constraint keeps the state change flow centralized and traceable.

Changes Through Pure Reducers

A reducer is a pure function that receives state and action, then returns new state. Pure means: same input, same output; it never reads global data; and it never performs side effects.

JSA reducer is a pure function
function counterReducer(state = { count: 0 }, action) {
  if (action.type === "counter/increment") {
    return { count: state.count + 1 }
  }
  return state
}

counterReducer(state, action) doesn't modify the old state; instead it returns a new object { count: state.count + 1 }. Because it's pure, a reducer is easy to test and its results are always deterministic.

How It Works Behind the Scenes

The Complete Data Cycle

While the application runs, this cycle repeats continuously:

Redux data cycle
dispatch(action) -> reducer -> store (new state) -> notify subscriber -> UI re-render
  • The component calls dispatch(action).
  • The reducer computes new state.
  • The store saves the new state and notifies all subscribers.
  • React Redux triggers re-renders in subscribed components.

Note: store.subscribe(fn) is a low-level API you usually won't touch directly — react-redux connects it through hooks like useSelector.

The Role of Middleware

Middleware sits between dispatch and the reducer. It can observe actions, modify them, stop them, or run side effects. The most popular examples are Redux Thunk for async functions and createListenerMiddleware for centralized reactions.

JSWhere middleware sits in the flow
dispatch(action) -> [middleware...] -> reducer -> store

Without middleware, dispatch only accepts action objects. With thunk, dispatch can also accept functions containing async logic — this is the foundation of createAsyncThunk, which we'll learn in episode 6.

Immer for Safe Reducers

Writing immutable updates by hand is error-prone. Redux Toolkit injects Immer into reducers, so you can write code that looks like direct mutation:

JSSafe draft mutation with Immer
const slice = createSlice({
  name: "counter",
  initialState: { count: 0 },
  reducers: {
    increment(state) {
      state.count += 1
    },
  },
})

state.count += 1 looks like mutation, but Immer creates a temporary draft and produces a new state object behind the scenes. We'll explore Immer's draft and frozen state mechanics more deeply in episode 19.

The Main Redux Toolkit Components

To make navigation easier for the rest of the series, here's a map of the main RTK components:

  • configureStore: creates a store with default middleware and DevTools.
  • createSlice: writes a reducer + action creators in one block.
  • createAsyncThunk: async actions with pending, fulfilled, rejected lifecycle.
  • createEntityAdapter: manages collections of normalized entities.
  • createListenerMiddleware: reactive, action-based middleware.
  • RTK Query: createApi for fetching and caching server data.

Each of these will be covered specifically in episodes 3 through 22. For now, just memorize their names and roles.

Conclusion

Episode 2 gives you the lens to read the rest of the series: Redux's three principles, unidirectional data flow, middleware as the home of side effects, Immer as the reducer safety net, and the RTK component map.

Key takeaways:

  • One store is the single source of truth for all application state.
  • State only changes through dispatched actions.
  • A reducer is a pure function that returns new state.
  • The data cycle: dispatch, reducer, store, notify, then re-render.
  • Middleware sits between dispatch and the reducer.
  • Immer makes writing reducers feel like mutation without violating immutability.

In the next episode, episode 3, you'll start practicing: store setup and Provider — creating your first store with configureStore, wrapping the app with Provider from react-redux, and laying out the app and features folder structure we'll use throughout this series.