Request/Success/Failure Pattern in Redux (2 Part series) |
---|
Part 1: Using request/success/failure pattern in Redux to handle async actions |
Part 2: Simplifying request/success/failure model for async action in Redux for large applications |
A lot of times while making the API calls, we face the challenge; where based on different points of time of API call, we need to show different UI or handle other scenarios in a project. There are a lot of approaches to do the same. However, there is one which is the best and is widely used in many organizations. We will discuss about the same in this article.
This is a 2 part article
where we will see what is this pattern and how by using capabilities of javascript we can modify this pattern to scale our project easily and keep the code clean in the longer run.
The pattern!
When any async action or API call is made, there are mainly 3 states:
- Request State - The async action or API call is in process
- Success State - The async action or API call is successful and gets some data
- Failure State - The async action or API call is errored/failed due to some reasons
Based on these 3 states, we create functions and follow certain conventions etc to achieve the desired result.
Things needed here:
- action functions
- type strings
- reducer function(s)
We pick the main action verb here and append request/success/failure at the end to follow consistent naming convention.
Let's take an example where we will be making an API call to get a list of users. For each of above listed cases, we will create an action and a type each. Corresponding to cases in above list for get list of users, we have now the following actions and types now:
And the corresponding reducer and the initial state will look something like this
How it works
When we need to make an api call we will dispatch the request action. It will make isLoading to true and we can use it to show an appropriate message/loader in the screen. As soon as the api call is finished; it will either be in successful or failure state. For each of these, we will dispatch either success or failure which will update the data in the reducer (in data or error variables respectively) as well as make isLoading to false and loaded to true.
The various variables such as isLoading, loaded etc can be now be used in our component for the desired interactions or functionalities.
Why to stick to this pattern
There are several advantages for this pattern and it closely follows all the good practices recommended in any software development. Few of them are:
- Readability and Maintainability: Since we are following a fixed pattern for naming, code becomes a lot more readable. request/success/failure model communicates properly the state in which API is and reduces mental overhead.
- Code Scalability: This structure is highly scalable. We will see how in our next article where we will reuse this structured format to extend this pattern for multiple API calls and avoid a lot of code repetition.
- Control and Precise Hooks: This pattern also gives us more control. Once implemented, we have hooks at various point in API call to update the UI. Variables like isLoading and loaded give us control over UI whereas actions give control over how to save data in reducer.
Integrating the pattern with middlewares
The pattern also fits in very nicely which libraries such as redux-thunk or redux-saga.
An example here will demonstrate on how to use the same with redux-saga
and the same can be done easily with thunk as well.
Adding additional actions/types (if needed)
There could be some scenarios where devs might need to reset the data to initial state. In that case, we can add additional action accordingly.
That's it about the pattern. So simple and sleek and yet so powerful!!
Extending the pattern for multiple API calls
The same approach can be extended now for multiple API calls. The only issue is that if one have a lot of api calls, there will be 3 actions, 3 types and 1 reducer per API call. It means that there will be a lot of repetitive code involved and there will be multiple reducers and logic to merge them.
Well, not to worry about; we can simplify this by using the functional nature of javascript.
For now, Part 1 of the article ends here. Keep following this space and I will update the link for Part 2 in here shortly!
Edit: Part 2 of the article is published here.
Top comments (7)
The development team at my work use this pattern across our React apps. We also place all the action type variables in one file then import where needed. That way any developer looking at a project for the first time can see all API interactions in the app at a glance.
Hi Aileen,
Yes this is a very good approach. However, there is a lot of boilerplate code involved. I hope your team uses some libraries or utilities to avoid the boilerplate code. I have also published Part 2 of the article which shows a way to simplify the code a lot. You can read it here
Hi,
You can try to start implementing redux toolkit and redux thunk. It will simplify the aforementioned boiler plate code.
Hi Zhitomir,
Thanks for your valuable inputs.
Our team doesn't use this code directly. We have a lot of utility functions (inspired by redux-toolkit and redux-actions) which reduce this repetitive boilerplate code to zero. You can read the article published over here
Hi, thank you for your response.
Well, I have already used it in some of our projects, just shared my personal experience and this is one of the trends right now. I can verify that aforementioned pattern is easy to implement but of'course there are always alternatives. We try as much as possible to encapsulate each slice of the state in separate modul with all interfaces.
old, but still viable. I always use this pattern in my http-verb methods. +1
Yes, it is a very good approach.