When your application grows, you may find several parts of your stores doing the same kinds of work for different types of data, ideally we want to reduce duplication by reusing the same common logic for each data type. It can also happen that you want to have multiple “instances” of the same type of data.
Let’s say that we’re creating an application that has two pages which each display a list of users. Each list displays a different set of users, for example: administrators or editors. Each list can be sorted and we can select a user. Reducers for the pages can possibly look like this:
First the administrator reducer.
Then the editor reducer:
After that we can use combineReducers
to setup our state
For each reducer we’ve done a few things:
While there is nothing wrong with each individual reducer, this code is not really DRY. Since the structure of each reducer is exactly the same, we can abstract the code and create one reducer that can be used by both the administrator and the editor state.
We can make our reducer more abstract by using user
in the keys of our state, instead of editor
or administrator
. Our reducer can then possibly look like this:
Setting up our state can then be done like this
There however is a problem with this setup. Since combineReducers
will call each reducer that has the same action. If we would dispatch an action of {"type": "sort"}
it will actually sort both the administrator and the editor state, not just the one you would like to sort. In order to achieve the desired behavior we need some way to wrap the reducer logic so that we can ensure that only the reducer we care about is updated. This can be achieved using a higher-order reducer function.
A higher-order reducer is a function that takes a name as an argument and returns a new reducer function as a result. We can use this pattern to create new instances of a reducer. This higher-order reducer might look like this:
We can now use this function in our combineReducers
function to generate multiple instances of the reducer:
Now suffix your action names so they correspond with the new names in your reducer and your good to go.
Using the higher-order reducer pattern we are able to dispatch actions that will affect only the portion of the state that we care about and we can create multiple instances of our data in a DRY way. We prevent duplication of reducer logic which results in a more maintainable codebase.
Thanks for reading. In my next post I will write about reusing effect logic. This was my first post, any feedback? Let me know.