Store, Actions, and Reducers
In this page:
Anatomy of an Action
An action is just a plain JavaScript object with, at minimum, a type field — a string describing what happened, like 'cart/addItem'. Any additional data needed to process that action (like which item was added) is usually included as a payload field.
Note: A common convention is 'domain/eventName' for type strings, like 'cart/addItem' or 'auth/login', to avoid collisions between unrelated actions.
Warning: Actions only describe what happened — dispatching one doesn't change anything by itself until a reducer handles it.
Example: Anatomy of an Action
// Run in your local React project (npm install required)
const addItemAction = {
type: 'cart/addItem',
payload: { id: 1, name: 'Book' },
};
store.dispatch(addItemAction);
Anatomy of a Reducer
A reducer is a function taking the current state and an action, and returning the next state. It must be pure — given the same inputs, it always produces the same output, with no API calls, no mutations, and no randomness.
Note: Use a switch statement (or an if/else chain) on action.type to decide which piece of logic to run.
Warning: Forgetting a default case that returns the unchanged state means an unrecognized action type could return undefined, corrupting the store.
Example: Anatomy of a Reducer
// Run in your local React project (npm install required)
function cartReducer(state = { items: [] }, action) {
switch (action.type) {
case 'cart/addItem':
return { items: [...state.items, action.payload] };
default:
return state;
}
}
The One-Way Data Flow
Redux enforces a strict cycle: a component dispatches an action, the reducer computes new state from it, the store updates, and any component reading that state re-renders with the new value. This predictable, one-directional flow is what makes Redux apps easier to debug and reason about.
Note: Redux DevTools lets you literally see every dispatched action and the resulting state change, which is invaluable for debugging this flow.
Warning: Nothing in this cycle updates the UI directly — components only re-render because they're subscribed to the store via useSelector, reacting to the state change.
Example: The One-Way Data Flow
// Run in your local React project (npm install required)
// 1. Component dispatches:
dispatch({ type: 'cart/addItem', payload: { id: 2, name: 'Pen' } });
// 2. Reducer computes new state (shown above)
// 3. Store updates internally
// 4. useSelector(state => state.cart.items) re-runs in any subscribed component
// 5. That component re-renders with the new items array
- Giving two different actions the same type string, causing one to accidentally trigger the other's logic.
- Putting side effects (API calls, timers) directly inside a reducer instead of in middleware or a component.
- Returning undefined from a reducer's default case instead of returning the unchanged state.
- An action is a plain object with a type field describing what happened, plus optional data (the payload).
- A reducer is a pure function: (state, action) => newState, with no side effects.
- The store's dispatch(action) function is the only way to trigger a state update.
- Reducers must always return a value, including an unchanged state for actions they don't recognize.
Requires npm install redux (or @reduxjs/toolkit) — not available via CDN in this sandbox.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: