Redux By Example: Part 3
Introducing the important concepts of action creator and selector.
This article is part of a series (starting with Redux By Example: Part 1) of articles that, through a series of progressively more complicated examples, illustrates a number of core Redux concepts. These examples are provided as Node.js applications; without any browser or React complexities.
Normalizr
note: While the use of normalizr is consistent the tutorials provided by Dan Abramov (developed Redux), I have begun to move away from using it with projects with normalized APIs (unnecessary extra overhead).
While we implemented each of the CRUD operations through the previous examples, we are going to use the normalizr module to streamline the reducer’s code.
The crux of the problem in the previous examples is that the structure of the action’s value
differs across FETCH
and the other ( ADD
, DELETE
, and UPDATE
) actions. In the case of FETCH
it is an array of object and the others it is an object.
The normalizr normalize
function converts both the arrays and objects (used in the action’s value
) into a common data structure that parallels our store structure, for example for FETCH
it becomes.
{
entities: {
items: {
m: {
id: 'm',
name: 'mango',
description: 'Sweet and sticky'
},
n: {
id: 'n',
name: 'nectarine',
description: 'Crunchy goodness'
}
}
},
result: [ 'm', 'n' ]
}
and for UPDATE
it becomes
{
entities: {
items: {
m: {
id: 'm',
name: 'mango',
description: 'Sweet and super sticky'
}
}
},
result: 'm'
}
Run the example with the command node dist/normalizr.js
.
Looking at the example’s source code:
- Compared to our earlier examples, the reducers are greatly simplified (got rid of the need for the loop and
map
). - Incidentally, we updated the
FETCH
action to completely replace theids
instead of appending them. As theFETCH
is typically done once early in the application startup, this distinction is more of a formality than a necessity. But, should one choose toFETCH
later it is important to not double-up on entries.
Action Creators
Keeping in mind that the purpose of Redux is to provide a protected mechanism to manage application state, it is important to decouple the state management implementation details from the rest of the application.
Thinking of the previous example, having to use normalizr and the schema objects when dispatching events (with React, done in components) is such a tight coupling.
The solution is to create functions, action creators, that encapsulate the implementation details.
Run the example with the command node dist/action-creators.js
.
Looking at the example’s source code:
- This application has two action creators,
fetch
andupdate
that shield the rest of the application from the implementation details, i.e.,normalize
,itemsSchema
, anditemSchema
.
Selectors
Similar to the problem that action creator solve, selectors encapsulate the implementation details when responding to events.
Run the example with the command node dist/selectors.js
.
Looking at the example’s source code:
- This application has two selectors,
getItem
andgetItems
that shield the rest of the application from the implementation details, e.g.,byId
andids
.
The Next Part
In the final part, Redux by Example: Part 4, we will address a subtle problem with the getItems
implementation and further encapsulate handling of the items (the fruits).