Maybe Better Reducers- 5 mins
In summary, a Redux app is a container for a immutable data structure representing the current state. That container accepts actions which are just plain objects describing user intent. Once an action is accepted, a reducer function runs and creates the next state based on the action and the current state. The reducer function then is the core of a Redux app.
Redux doesn’t impose any restrictions on the way you define your reducer, as long as it comforms with the following signature:
reducer :: (State, Action) -> State
Most examples all over the internet - including the one in the official documentation - will suggest you to define the reducer function using a
switch statement on the action type.
This is literally the reducer suggested in the Redux official docs. I wonder if people are really following this approach (I hope not). As you can see, the code above is very busy to read and not at all modular. Hopefully, we can do better than that. Let’s see.
The algorithm of the reducer we just saw can be described as:
- It chooses a behavior by pattern-matching the
- Then, it executes the matching behavior returning a new state;
- In case there is no match, it returns the unchanged state.
The Maybe version of our reducer relies on an addional step: we extracted all the action-specific behaviors to separate functions, which are accessible in a map we called
Now each action handler is an independent function with a single and succint responsibility. The same applies to the reducer which uses the Maybe container to drive the logic around the pattern matching. And just that.
Those who are not familiar with Maybe can think of it as a container for a value that might or might not be present. In our example, an action type might or might not be found in the map. The
.fromNullable constructor does that for us: it gets an actual function or it gets nothing in case there is no match. Next, we call
.map which executes a present behavior. The interesting thing about
.map is that is does not run in case the Maybe is holding an empty value. The last call
.getOrElse returns the value from inside the container if the value is present. Otherwise, it uses the default argument, which in our case is the unchanged
Redux is great not only because it is so simple and concise but also because it is based on strong Functional Programming concepts. Additionally, Maybes, Eithers, Validations and other containers from Folktale are very handy in a diversity of situations, but specially useful when it comes to functional error handling. The reducer example is just a small demonstration on how well the Redux idea fits with other FP based libs. For a complete and more detailed example take a look at my Redux + Folktale + Ramda Todo App.
The idea behind this post inspired the development of the Redux Definer NPM package. Pretty handy to avoid reducer boilerplate code. In case you also want to avoid action creators boilerplate, you can go straight to Redux Actions.