How to Scale a Redux Application with Redux-Action-Recompose
One of biggest challenges that you may face with a Redux application is having to write duplicated action-creators and reducers for common actions, such as data fetching. There is a partial solution proposed in the Redux Documentation for reusing reducers. However, you will realize that you still have to write duplicated action-creators or complicated action-creators.
You will experience similar problems if you want to use a shared component at different places. For instance, if you have a generic datatable component, which allows sorting, row selection, data fetching, pagination, etc.
However, if you want to use the datatable in multiple places, you will realize that you have to repeat the action-creators and reducers.
This article will introduce redux-action-recompose and explain how it will help you to scale your application by assisting you to reuse common action-creators and reducers. The article will use an fetch data example to illustrate how redux-action-recompose work.
Suppose you need to write an application with two components, users and todos. Both component will need to fetch and display fetched data. You might write your async action-creators (fetchData) and reducers (asyncHandlers) as follows: (example follows The Ducks File Structure):
In an ideal world, both users and todos would simply need to import and use the async action-creators and reducers as something like these:
However, this will not work. When either users or todos calls fetchData(…), the async reducer would be fired for both components, because they would be using the same action {type: ‘FETCH_DATA_REQUEST’} and {type: ‘FETCH_DATA_RESPONSE’, …}. Therefore, both users and todos have to implement their own version of the action-creators and reducers with different action types, and you will end up with many duplicated action-creators and reducers.
Redux-Action-Recompose is an npm package that will solve this problem for you. Redux-Action-Recompose exposes two functions, ‘decorateHandlers’ and ‘decorateActions’. You will simply use these two functions to decorate your action-creators and reducers with unique labels for the users and todos.
For instance, to use the shared action-creators and reducers in the users component. You simply need to decorate the asyncActions and asyncHandlers with a label ‘users’.
Now, all of the action types in the action-creators and reducers are namespaced. For instance, when this.props.fetchData(…) is called in users component, it will dispatch {type: ‘users/FETCH_DATA_REQUEST’} and {type: ‘users/FETCH_DATA_RESPONSE’, …}, instead of the original {type: ‘FETCH_DATA_REQUEST’} and {type: ‘FETCH_DATA_RESPONSE’, …}. The users reducers will also be listening to the namespaced action types.
In your todos component, you will simply decorated the action-creators and reducers with a different label (i.e. ‘todos’). It will then get a unique version of the async action-creators and reducers by having a unique label.
In addition, another important advantage of Redux-Action-Recompose is that it will also allow you to easily overwrite and override your action-creators and reducers by using the spread operator. For instance,
decorateHandlers({…asyncHandlers, …additionalHandlers}, ‘todos’)
decorateActions({…asyncActions, …additionalActions}, dispatch, ‘todos’)