Simplifying request/success/failure model for async action in Redux for large applications (Part 2/2)
Now that we have learnt the pattern to skillfully handle the async actions in redux, let’s dive deeper into how to simplify the same to make code more cleaner and scalable. For those who missed the Part 1, please read here.
Why to simplify?
Notice that, we have written a lot of boilerplate code just to handle one API call. Also, code will become repetitive in nature for multiple calls which contradicts with DRY and other software writing methodologies.
Process of simplification
We will pick each of our actions, types, reducer(s) and simplify them one by one.
Actions and Types
This is how we write our actions and types in this approach:
Observe here that there are 3 actions and 3 types. And the same pattern will be repeated for each API call. Imagine if there are 10 API calls. It means there will be 30 actions and 30 types to be manually written. To avoid this, we will write a helper function that will take one input string and return all the these. The function will look something like this:
If we use the above function then the entire logic for actions and types will be reduced to just one single line
const getUsersList = actionCreator('GET_USERS_LIST')
This will give all the required actions and types.
Next question is how to use all these. The answer is pretty simple. getUsersList is an object, so the relevant actions and types will be following:
- getUsersList.request instead of getUsersListRequest
- getUsersList.success instead of getUsersListSuccess
- getUsersList.failure instead of getUsersListFailure
- getUsersList.REQUEST instead of GET_USERS_LIST_REQUEST
- getUsersList.SUCCESS instead of GET_USERS_LIST_SUCCESS
- getUsersList.FAILURE instead of GET_USERS_LIST_FAILURE
Reducer
This is how current reducer looks like and it is only usable for one api call.
The reducer will now be modified to this:
Note we have done 2 things here:
- We have clubbed the relevant switch cases together and transferred the logic to update store to a new reducerHandler function.
- We have added a key
usersList
which will contain entire state for that particular API. This will make sure that same reducer can be used for multiple API calls.
Let see the definition of reducerHandler
function (helper function to be written only once) now:
Though we have added few functions which might seem that code is increased but observe that the task of creating multiple actions, types and reducers is reduced to few lines. Now if we have to do a new API call, just one line is added to create actions & types and few lines added in reducers instead of writing about 50 lines and adding a new reducer. This simplifies the code a lot.
Integrating with middlewares
The other part of calling from sagas and dispatching actions will remain same. Let’s see an example:
This will help to keep code clean and help developers to increase productivity and focus on other areas.
Exhaustive example
Just to show, how this approach works here is an example with just 3 API calls:
Old Approach
Simplified new approach
If you see, effort is almost reduced to 1/3rd with same desired effect.
Are we done? Yes! At this point, a lot of repetitive logic is reduced and code is simplified. Can we simplify this further? Maybe, but I won’t advice it. We can even write a wrapper to avoid writing actions and reducers at all but it has chances of doing more harm than good.
Are there any libraries which can provide these utilities?
Yes, there are a few libraries which can do this for you but one should always think before adding extra libraries. Libraries increase the bundle size and then one has to maintain dependencies etc. That’s why for simpler parts like this, writing our own logic seems preferable.
Hope, you like this article. Please like, share and comment to discuss anything which can make this approach better.
Originally published at https://dev.to on December 18, 2020.