Redux architecture revolves around a strict unidirectional data flow.
The data lifecycle in any Redux app follows these 4 steps:
-
You call
store.dispatch(action)
. An action is a plain object describing what happened. -
The Redux store calls the reducer function you gave it. The store will pass two arguments to the reducer: the current state tree and the action. Note that a reducer is a pure function. It only computes the next state. It should be completely predictable: calling it with the same inputs many times should produce the same outputs. It shouldn't perform any side effects like API calls or router transitions. These should happen before an action is dispatched.
-
The root reducer may combine the output of multiple reducers into a single state tree. How you structure the root reducer is completely up to you. Redux ships with a
combineReducers()
helper function, useful for “splitting” the root reducer into separate functions that each manage one branch of the state tree. -
The Redux store saves the complete state tree returned by the root reducer. This new tree is now the next state of your app! Every listener registered with
store.subscribe(listener)
will now be invoked; listeners may callstore.getState()
to get the current state. Now, the UI can be updated to reflect the new state. If you use bindings like React Redux, this is the point at whichcomponent.setState(newState)
is called.
The reducers updates the state according to some actions.
Things you should never do inside a reducer:
- Mutate its arguments;
- Perform side effects like API calls and routing transitions;
- Call non-pure functions, e.g. Date.now() or Math.random().
The reducer must be pure. Given the same arguments, it should calculate the next state and return it. No surprises. No side effects. No API calls. No mutations. Just a calculation.
The reducer composition is the fundamental pattern of building Redux apps.
The store has the following responsibilities:
- Holds application state;
- Allows access to state via
getState()
; - Allows state to be updated via
dispatch(action)
; - Registers listeners via
subscribe(listener)
; - Handles unregistering of listeners via the function returned by
subscribe(listener)
.
It's important to note that you'll only have a single store in a Redux application. When you want to split your data handling logic, you'll use reducer composition instead of many stores.