Reducers
Redux reducers are usually written using switch
statements, with each case handling a specific action type.
This pattern works reasonably well, but also has drawbacks:
There is some required boilerplate, specifically the wrapping
switch (action.type)
block and thedefault: return state
case (the latter is easy to forget when writing a new reducer).You have to remember the idiosyncrasies of JavaScript's
switch
statements, such as the additional braces needed to define variables which should be local to a case (e.g.,case 'multiply': { const factor = action.payload; … }
).In TypeScript, ensuring that
action
is typed correctly in everyswitch
branch requires a considerable amount of extra typing effort, often including union types of all types of actions in the app.
Redux Preboiled has several helpers for constructing reducers which address these shortcomings. Here is how you might write the example reducer above with Preboiled:
This guide details Preboiled's reducer helpers and how they relate, plus how they help you reduce typing effort if you use TypeScript.
Reducing Specific Actions
Preboiled's onAction
helper generates reducer-like functions which update the state only in response to actions of a specific type.
Note that the functions returned by onAction
are not "proper" reducers - they don't provide an initial state. For this reason, we prefer to call them sub-reducers as they are meant to be embedded into actual reducer functions. Later in this guide, we'll see how this can be done easily with the withInitialState
and chainReducers
helpers.
Integration with createAction
createAction
If you generate your action creators using createAction
(as described in the Actions guide), you can pass them directly to onAction
in place of their corresponding action types.
This removes a bit of noise as you don't need to write onAction(multiply.type, ...)
.
If you use TypeScript, there is another benefit: based on the action creator's type, the TypeScript compiler automatically infers the type of the action
argument passed to the state update function. This helps you prevent mistakes, like in the following example:
You'll also get better autocompletion in IDE's and editors which understand TypeScript, such as Visual Studio Code or WebStorm.
Providing Initial State
Using the withInitialState
helper, you can generate reducers which return a specific value as initial state.
There are two ways to use this helper. The first is to pass an initial state value and a reducer-like state update function. The resulting reducer will forward all calls to that function unless called with an undefined
state (i.e., during Redux store initialization), in which case it returns the given initial state value instead. One use case for this variant is to make a full reducer from a single onAction
sub-reducer:
Alternatively, you can specify only an initial state when calling withInitialState
. In this case, the reducer will simply return any non-undefined
state unchanged.
This second version of withInitialState
is useful for reducer chains, which we'll look at next.
Chaining Reducers
Redux comes with the combineReducers
function, which allows you to compose multiple reducers for different state slices. Preboiled complements this with chainReducers
, which is about composing reducers for the same state slice.
chainReducers
turns a sequence of (sub-)reducers into a pipeline, or "chain", where the output state of one reducer becomes the input state for the next. Let's look at a silly, but illustrative example:
For every incoming action, the reducer above first forwards the call to uppercaseReducer
, the first reducer in the chain. It then passes the resulting state to the second reducer (lowercaseReducer
), whose return value finally becomes the state returned by the chained reducer itself.
Note that for the chain to be a proper reducer, at least one of its functions (usually the first one) must return an initial state if called with an undefined
state. A common pattern is to start the chain with a withInitialState
reducer, e.g.:
Replacing switch
switch
As shown in this guide's introduction, you can chain multiple onAction
sub-reducers as a replacement for the switch
reducer pattern.
This works because each sub-reducer only reacts to one specific type of action, and leaves the state unchanged for all others; incoming actions will thus pass through the chain until they reach the matching sub-reducer, or leave the chain without a state change (the equivalent of default: return state
in the switch
pattern).
Next Steps
In addition to defining actions and reducers, Redux Preboiled also helps you with testing your Redux code. See the Testing guide.
Last updated