Last time we added some tests to our codebase to illustrate how our implementation of Reducks works against Redux. This time I want to look to expanding the features of Reducks and today it’s to support multiple reducers.
Understanding multiple reducers
In a suitably complex Redux application you’re going to want to break down your reducer into smaller reducers. Let’s take our reducer code (abridged):
Now you can see where this is going, we’re going to have a very large
switch statement, resulting ugly code. So you might want to split this down into smaller functions, but there’s another reason you might want to use multiple reducers is that you want to split your
state apart. This might be done because, taking our example above, the visibility filter is independent from the lost of todos, so we could have reducers that only concerns itself with the
visibilityFilter. Now we would have reducers looking like:
Awesome, we’ve got a couple of smaller reducers, but how do we use them given that
createStore takes a single reducer?
This is a function produced by Redux that takes an object that has all our reducers as properties, like so:
combineReducers function returns is a new function that takes two arguments,
action, which looks very much like a reducer doesn’t it!
Now here’s the tricky thing, the object you pass into
combineReducers is the shape of the
state that you’ll have for your application, but each reducer only has access to their tree, meaning that the
visibliltyFilter property is all that is accessible to the
Implementing the function
So let’s get started on our implementation:
We’re going to need all those reducer functions so let’s deconstruct our object and get them:
So we’ve got our reducer names, we’re returning our new reducer function, let’s implement it:
Ok, how does it work? Well we take our reducer names (
keys) use the reduce function with an initial state of an empty object. For each reducer we:
- Get the current state (which will be the initial state provided to the store, or
undefinedis we use the default argument set on the new reducer function)
- Invoke the reducer with the current state and the action
- Return the new state
Because we’re using the
reduce array method we then combine all the new states into a new object, using the name as the reducer for the property. So after our first run (the
@@INIT action) our
state would look like:
Pretty nifty! Since we use the reduce method we create a new
state object every single time, meaning that we don’t have to worry about mutating the previous state object (sure a reducer could do that, but we don’t :P).
And then we’re done.
And that is how we implement multiple reducers in Reducks. Ultimately it’s a bit of a misnomer, we still only have a single reducer as far as the
store knows, but internally we’re breaking apart state, using different functions to handle different parts of the state tree and creating a single application state.
combineReducers function doesn’t have to be done this way, I’m just looking at how Redux does it, but if you didn’t want the reducers to handle each branch, instead have access to the whole tree you could use an array of reducers and loop over them, providing the state to each one.
I’ve gone ahead and updated the code, you can find it at this tag, I’ve also taken it one step further than we looked at above and split the incomplete and complete todos apart. I’ve also updated the tests to understand the new structure.