Redux has greatly helped in reducing the complexities of state management. Its one way data flow is easier to reason about and it also provides a powerful mechanism to include middlewares which can be chained together to do our biding. One of the most common use cases for the middleware is to make async calls in the application. Different middlewares like redux-thunk, redux-sagas, redux-observable, etc are a few examples. All of these come with their own learning curve and are best suited for tackling different scenarios.
But what if our use-case is simple enough and we don’t want to have the added complexities that implementing a middleware brings? Can we somehow implement the most common use-case of making async API calls using only redux and javascript?
The answer is Yes! This blog will try to explain on how to implement async action calls in redux without the use of any middlewares.
So let us first start by making a simple react project by using create-react-app
CODE: https://gist.github.com/velotiotech/cd1846d7d2d700bd32e07febf0c21474.js
Also we will be using react-redux in addition to redux to make our life a little easier. And to mock the APIs we will be using https://jsonplaceholder.typicode.com/
We will just implement two API calls to not to over complicate things.
Create a new file called api.js .It is the file in which we will keep the fetch calls to the endpoint.
CODE: https://gist.github.com/velotiotech/8b09fe12143abb17fad99b012df44cc4.js
Each API call has three base actions associated with it. Namely, REQUEST, SUCCESS and FAIL. Each of our APIs will be in one of these three states at any given time. And depending on these states we can decide how to show our UI. Like when it is in REQUEST state we can have the UI show a loader and when it is in FAIL state we can show a custom UI to tell the user that something has went wrong.
So we create three constants of REQUEST, SUCCESS and FAIL for each API call which we will be making. In our case the constants.js file will look something like this:
CODE: https://gist.github.com/velotiotech/22b9c03ef109e1fae76af7c7df15a094.js
The store.js file and the initialState of our application is as follows:
CODE: https://gist.github.com/velotiotech/c197bc2c441e3ad48e89836e1df75f61.js
As can be seen from the above code, each of our APIs data lives in one object the the state object. Keys isLoading tells us if the API is in the REQUEST state.
Now as we have our store defined, let us see how we will manipulate the statewith different phases that an API call can be in. Below is our reducers.js file.
CODE: https://gist.github.com/velotiotech/f9958986d542fbfece03896363de7719.js
By giving each individual API call its own variable to denote the loading phase we can now easily implement something like multiple loaders in the same screen according to which API call is in which phase.
Now to actually implement the async behaviour in the actions we just need a normal JavaScript function which will pass the dispatch as the first argument. We pass dispatch to the function because it dispatches actions to the store. Normally a component has access to dispatch but since we want an external function to take control over dispatching, we need to give it control over dispatching.
CODE: https://gist.github.com/velotiotech/a05767f7eb8b438e6c021f6db15fa428.js
And a function to give dispatch in the above function’s scope:
CODE: https://gist.github.com/velotiotech/b5c2ff963bac61de03e278cde5f453cd.js
So now our complete actions.js file looks like this:
CODE: https://gist.github.com/velotiotech/8d2f6990426dfa0c25fb9e7e197b35e6.js
Once this is done, all that is left to do is to pass these functions in mapDispatchToProps of our connected component.
CODE: https://gist.github.com/velotiotech/623d090799ac78998691d02dada74174.js
Our App.js file looks like the one below:
CODE: https://gist.github.com/velotiotech/b2d3722cc183e59b9a632c8292a2fa86.js
This is how we do async calls without middlewares in redux. This is a much simpler approach than using a middleware and the learning curve associated with it. If this approach covers all your use cases then by all means use it.
Conclusion
This type of approach really shines when you have to make a simple enough application like a demo of sorts, where API calls is all the side-effect that you need. In larger and more complicated applications there are a few inconveniences with this approach. First we have to pass dispatch around to which seems kind of yucky. Also, remember which call requires dispatch and which do not.
The full code can be found here.