• United States+1
  • United Kingdom+44
  • Afghanistan (‫افغانستان‬‎)+93
  • Albania (Shqipëri)+355
  • Algeria (‫الجزائر‬‎)+213
  • American Samoa+1684
  • Andorra+376
  • Angola+244
  • Anguilla+1264
  • Antigua and Barbuda+1268
  • Argentina+54
  • Armenia (Հայաստան)+374
  • Aruba+297
  • Australia+61
  • Austria (Österreich)+43
  • Azerbaijan (Azərbaycan)+994
  • Bahamas+1242
  • Bahrain (‫البحرين‬‎)+973
  • Bangladesh (বাংলাদেশ)+880
  • Barbados+1246
  • Belarus (Беларусь)+375
  • Belgium (België)+32
  • Belize+501
  • Benin (Bénin)+229
  • Bermuda+1441
  • Bhutan (འབྲུག)+975
  • Bolivia+591
  • Bosnia and Herzegovina (Босна и Херцеговина)+387
  • Botswana+267
  • Brazil (Brasil)+55
  • British Indian Ocean Territory+246
  • British Virgin Islands+1284
  • Brunei+673
  • Bulgaria (България)+359
  • Burkina Faso+226
  • Burundi (Uburundi)+257
  • Cambodia (កម្ពុជា)+855
  • Cameroon (Cameroun)+237
  • Canada+1
  • Cape Verde (Kabu Verdi)+238
  • Caribbean Netherlands+599
  • Cayman Islands+1345
  • Central African Republic (République centrafricaine)+236
  • Chad (Tchad)+235
  • Chile+56
  • China (中国)+86
  • Christmas Island+61
  • Cocos (Keeling) Islands+61
  • Colombia+57
  • Comoros (‫جزر القمر‬‎)+269
  • Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)+243
  • Congo (Republic) (Congo-Brazzaville)+242
  • Cook Islands+682
  • Costa Rica+506
  • Côte d’Ivoire+225
  • Croatia (Hrvatska)+385
  • Cuba+53
  • Curaçao+599
  • Cyprus (Κύπρος)+357
  • Czech Republic (Česká republika)+420
  • Denmark (Danmark)+45
  • Djibouti+253
  • Dominica+1767
  • Dominican Republic (República Dominicana)+1
  • Ecuador+593
  • Egypt (‫مصر‬‎)+20
  • El Salvador+503
  • Equatorial Guinea (Guinea Ecuatorial)+240
  • Eritrea+291
  • Estonia (Eesti)+372
  • Ethiopia+251
  • Falkland Islands (Islas Malvinas)+500
  • Faroe Islands (Føroyar)+298
  • Fiji+679
  • Finland (Suomi)+358
  • France+33
  • French Guiana (Guyane française)+594
  • French Polynesia (Polynésie française)+689
  • Gabon+241
  • Gambia+220
  • Georgia (საქართველო)+995
  • Germany (Deutschland)+49
  • Ghana (Gaana)+233
  • Gibraltar+350
  • Greece (Ελλάδα)+30
  • Greenland (Kalaallit Nunaat)+299
  • Grenada+1473
  • Guadeloupe+590
  • Guam+1671
  • Guatemala+502
  • Guernsey+44
  • Guinea (Guinée)+224
  • Guinea-Bissau (Guiné Bissau)+245
  • Guyana+592
  • Haiti+509
  • Honduras+504
  • Hong Kong (香港)+852
  • Hungary (Magyarország)+36
  • Iceland (Ísland)+354
  • India (भारत)+91
  • Indonesia+62
  • Iran (‫ایران‬‎)+98
  • Iraq (‫العراق‬‎)+964
  • Ireland+353
  • Isle of Man+44
  • Israel (‫ישראל‬‎)+972
  • Italy (Italia)+39
  • Jamaica+1876
  • Japan (日本)+81
  • Jersey+44
  • Jordan (‫الأردن‬‎)+962
  • Kazakhstan (Казахстан)+7
  • Kenya+254
  • Kiribati+686
  • Kosovo+383
  • Kuwait (‫الكويت‬‎)+965
  • Kyrgyzstan (Кыргызстан)+996
  • Laos (ລາວ)+856
  • Latvia (Latvija)+371
  • Lebanon (‫لبنان‬‎)+961
  • Lesotho+266
  • Liberia+231
  • Libya (‫ليبيا‬‎)+218
  • Liechtenstein+423
  • Lithuania (Lietuva)+370
  • Luxembourg+352
  • Macau (澳門)+853
  • Macedonia (FYROM) (Македонија)+389
  • Madagascar (Madagasikara)+261
  • Malawi+265
  • Malaysia+60
  • Maldives+960
  • Mali+223
  • Malta+356
  • Marshall Islands+692
  • Martinique+596
  • Mauritania (‫موريتانيا‬‎)+222
  • Mauritius (Moris)+230
  • Mayotte+262
  • Mexico (México)+52
  • Micronesia+691
  • Moldova (Republica Moldova)+373
  • Monaco+377
  • Mongolia (Монгол)+976
  • Montenegro (Crna Gora)+382
  • Montserrat+1664
  • Morocco (‫المغرب‬‎)+212
  • Mozambique (Moçambique)+258
  • Myanmar (Burma) (မြန်မာ)+95
  • Namibia (Namibië)+264
  • Nauru+674
  • Nepal (नेपाल)+977
  • Netherlands (Nederland)+31
  • New Caledonia (Nouvelle-Calédonie)+687
  • New Zealand+64
  • Nicaragua+505
  • Niger (Nijar)+227
  • Nigeria+234
  • Niue+683
  • Norfolk Island+672
  • North Korea (조선 민주주의 인민 공화국)+850
  • Northern Mariana Islands+1670
  • Norway (Norge)+47
  • Oman (‫عُمان‬‎)+968
  • Pakistan (‫پاکستان‬‎)+92
  • Palau+680
  • Palestine (‫فلسطين‬‎)+970
  • Panama (Panamá)+507
  • Papua New Guinea+675
  • Paraguay+595
  • Peru (Perú)+51
  • Philippines+63
  • Poland (Polska)+48
  • Portugal+351
  • Puerto Rico+1
  • Qatar (‫قطر‬‎)+974
  • Réunion (La Réunion)+262
  • Romania (România)+40
  • Russia (Россия)+7
  • Rwanda+250
  • Saint Barthélemy (Saint-Barthélemy)+590
  • Saint Helena+290
  • Saint Kitts and Nevis+1869
  • Saint Lucia+1758
  • Saint Martin (Saint-Martin (partie française))+590
  • Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)+508
  • Saint Vincent and the Grenadines+1784
  • Samoa+685
  • San Marino+378
  • São Tomé and Príncipe (São Tomé e Príncipe)+239
  • Saudi Arabia (‫المملكة العربية السعودية‬‎)+966
  • Senegal (Sénégal)+221
  • Serbia (Србија)+381
  • Seychelles+248
  • Sierra Leone+232
  • Singapore+65
  • Sint Maarten+1721
  • Slovakia (Slovensko)+421
  • Slovenia (Slovenija)+386
  • Solomon Islands+677
  • Somalia (Soomaaliya)+252
  • South Africa+27
  • South Korea (대한민국)+82
  • South Sudan (‫جنوب السودان‬‎)+211
  • Spain (España)+34
  • Sri Lanka (ශ්‍රී ලංකාව)+94
  • Sudan (‫السودان‬‎)+249
  • Suriname+597
  • Svalbard and Jan Mayen+47
  • Swaziland+268
  • Sweden (Sverige)+46
  • Switzerland (Schweiz)+41
  • Syria (‫سوريا‬‎)+963
  • Taiwan (台灣)+886
  • Tajikistan+992
  • Tanzania+255
  • Thailand (ไทย)+66
  • Timor-Leste+670
  • Togo+228
  • Tokelau+690
  • Tonga+676
  • Trinidad and Tobago+1868
  • Tunisia (‫تونس‬‎)+216
  • Turkey (Türkiye)+90
  • Turkmenistan+993
  • Turks and Caicos Islands+1649
  • Tuvalu+688
  • U.S. Virgin Islands+1340
  • Uganda+256
  • Ukraine (Україна)+380
  • United Arab Emirates (‫الإمارات العربية المتحدة‬‎)+971
  • United Kingdom+44
  • United States+1
  • Uruguay+598
  • Uzbekistan (Oʻzbekiston)+998
  • Vanuatu+678
  • Vatican City (Città del Vaticano)+39
  • Venezuela+58
  • Vietnam (Việt Nam)+84
  • Wallis and Futuna+681
  • Western Sahara (‫الصحراء الغربية‬‎)+212
  • Yemen (‫اليمن‬‎)+967
  • Zambia+260
  • Zimbabwe+263
  • Åland Islands+358
Thanks! We'll be in touch in the next 12 hours
Oops! Something went wrong while submitting the form.

How to Make Asynchronous Calls in Redux Without Middlewares

Abhishek Lohani

Full-stack Development

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

npx create-react-app async-redux-without-middlewares
cd async-redux-without-middlewares
npm start

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.

export const getPostsById = id => fetch(`https://jsonplaceholder.typicode.com/Posts/${id}`);
export const getPostsBulk = () => fetch("https://jsonplaceholder.typicode.com/posts");
view raw api.js hosted with ❤ by GitHub

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:

export const GET_POSTS_BY_ID_REQUEST = "getpostsbyidrequest";
export const GET_POSTS_BY_ID_SUCCESS = "getpostsbyidsuccess";
export const GET_POSTS_BY_ID_FAIL = "getpostsbyidfail";
export const GET_POSTS_BULK_REQUEST = "getpostsbulkrequest";
export const GET_POSTS_BULK_SUCCESS = "getpostsbulksuccess";
export const GET_POSTS_BULK_FAIL = "getpostsbulkfail";
view raw constants.js hosted with ❤ by GitHub

The store.js file and the initialState of our application is as follows:

import { createStore } from 'redux'
import reducer from './reducers';
const initialState = {
byId: {
isLoading: null,
error: null,
data: null
},
byBulk: {
isLoading: null,
error: null,
data: null
}
};
const store = createStore(reducer, initialState, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default store;
view raw store.js hosted with ❤ by GitHub

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.

import {
GET_POSTS_BY_ID_REQUEST,
GET_POSTS_BY_ID_SUCCESS,
GET_POSTS_BY_ID_FAIL,
GET_POSTS_BULK_REQUEST,
GET_POSTS_BULK_SUCCESS,
GET_POSTS_BULK_FAIL
} from "./constants";
const reducer = (state, action) => {
switch (action.type) {
case GET_POSTS_BY_ID_REQUEST:
return {
...state,
byId: {
isLoading: true,
error: null,
data: null
}
}
case GET_POSTS_BY_ID_SUCCESS:
return {
...state,
byId: {
isLoading: false,
error: false,
data: action.payload
}
}
case GET_POSTS_BY_ID_FAIL:
return {
...state,
byId: {
isLoading: false,
error: action.payload,
data: false
}
}
case GET_POSTS_BULK_REQUEST:
return {
...state,
byBulk: {
isLoading: true,
error: null,
data: null
}
}
case GET_POSTS_BULK_SUCCESS:
return {
...state,
byBulk: {
isLoading: false,
error: false,
data: action.payload
}
}
case GET_POSTS_BULK_FAIL:
return {
...state,
byBulk: {
isLoading: false,
error: action.payload,
data: false
}
}
default: return state;
}
}
export default reducer;
view raw reducers.js hosted with ❤ by GitHub

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.

const getPostById = async (dispatch, id) => {
dispatch({ type: GET_POSTS_BY_ID_REQUEST });
try {
const response = await getPostsById(id);
const res = await response.json();
dispatch({ type: GET_POSTS_BY_ID_SUCCESS, payload: res });
} catch (e) {
dispatch({ type: GET_POSTS_BY_ID_FAIL, payload: e });
}
};
view raw async_func.js hosted with ❤ by GitHub

And a function to give dispatch in the above function’s scope:

export const getPostByIdFunc = dispatch => {
return id => getPostById(dispatch, id);
}

So now our complete actions.js file looks like this:

import {
GET_POSTS_BY_ID_REQUEST,
GET_POSTS_BY_ID_SUCCESS,
GET_POSTS_BY_ID_FAIL,
GET_POSTS_BULK_REQUEST,
GET_POSTS_BULK_SUCCESS,
GET_POSTS_BULK_FAIL
} from "./constants";
import {
getPostsById,
getPostsBulk
} from "./api";
const getPostById = async (dispatch, id) => {
dispatch({ type: GET_POSTS_BY_ID_REQUEST });
try {
const response = await getPostsById(id);
const res = await response.json();
dispatch({ type: GET_POSTS_BY_ID_SUCCESS, payload: res });
} catch (e) {
dispatch({ type: GET_POSTS_BY_ID_FAIL, payload: e });
}
};
const getPostBulk = async dispatch => {
dispatch({ type: GET_POSTS_BULK_REQUEST });
try {
const response = await getPostsBulk();
const res = await response.json();
dispatch({ type: GET_POSTS_BULK_SUCCESS, payload: res });
} catch (e) {
dispatch({ type: GET_POSTS_BULK_FAIL, payload: e });
}
};
export const getPostByIdFunc = dispatch => {
return id => getPostById(dispatch, id);
}
export const getPostsBulkFunc = dispatch => {
return () => getPostBulk(dispatch);
}
view raw actions.js hosted with ❤ by GitHub

Once this is done, all that is left to do is to pass these functions in mapDispatchToProps of our connected component.

const mapDispatchToProps = dispatch => {
return {
getPostById: getPostByIdFunc(dispatch),
getPostBulk: getPostsBulkFunc(dispatch)
}
};

Our App.js file looks like the one below:

import React, { Component } from 'react';
import './App.css';
import { connect } from 'react-redux';
import { getPostByIdFunc, getPostsBulkFunc } from './actions';
class App extends Component {
render() {
console.log(this.props);
return (
<div className="App">
<button onClick={() => {
this.props.getPostById(1);
}}>By Id</button>
<button onClick={() => {
this.props.getPostBulk();
}}>In bulk</button>
</div>
);
}
}
const mapStateToProps = state => {
return {
state
};
}
const mapDispatchToProps = dispatch => {
return {
getPostById: getPostByIdFunc(dispatch),
getPostBulk: getPostsBulkFunc(dispatch)
}
};
view raw App.js hosted with ❤ by GitHub

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.

Get the latest engineering blogs delivered straight to your inbox.
No spam. Only expert insights.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings

How to Make Asynchronous Calls in Redux Without Middlewares

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

npx create-react-app async-redux-without-middlewares
cd async-redux-without-middlewares
npm start

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.

export const getPostsById = id => fetch(`https://jsonplaceholder.typicode.com/Posts/${id}`);
export const getPostsBulk = () => fetch("https://jsonplaceholder.typicode.com/posts");
view raw api.js hosted with ❤ by GitHub

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:

export const GET_POSTS_BY_ID_REQUEST = "getpostsbyidrequest";
export const GET_POSTS_BY_ID_SUCCESS = "getpostsbyidsuccess";
export const GET_POSTS_BY_ID_FAIL = "getpostsbyidfail";
export const GET_POSTS_BULK_REQUEST = "getpostsbulkrequest";
export const GET_POSTS_BULK_SUCCESS = "getpostsbulksuccess";
export const GET_POSTS_BULK_FAIL = "getpostsbulkfail";
view raw constants.js hosted with ❤ by GitHub

The store.js file and the initialState of our application is as follows:

import { createStore } from 'redux'
import reducer from './reducers';
const initialState = {
byId: {
isLoading: null,
error: null,
data: null
},
byBulk: {
isLoading: null,
error: null,
data: null
}
};
const store = createStore(reducer, initialState, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default store;
view raw store.js hosted with ❤ by GitHub

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.

import {
GET_POSTS_BY_ID_REQUEST,
GET_POSTS_BY_ID_SUCCESS,
GET_POSTS_BY_ID_FAIL,
GET_POSTS_BULK_REQUEST,
GET_POSTS_BULK_SUCCESS,
GET_POSTS_BULK_FAIL
} from "./constants";
const reducer = (state, action) => {
switch (action.type) {
case GET_POSTS_BY_ID_REQUEST:
return {
...state,
byId: {
isLoading: true,
error: null,
data: null
}
}
case GET_POSTS_BY_ID_SUCCESS:
return {
...state,
byId: {
isLoading: false,
error: false,
data: action.payload
}
}
case GET_POSTS_BY_ID_FAIL:
return {
...state,
byId: {
isLoading: false,
error: action.payload,
data: false
}
}
case GET_POSTS_BULK_REQUEST:
return {
...state,
byBulk: {
isLoading: true,
error: null,
data: null
}
}
case GET_POSTS_BULK_SUCCESS:
return {
...state,
byBulk: {
isLoading: false,
error: false,
data: action.payload
}
}
case GET_POSTS_BULK_FAIL:
return {
...state,
byBulk: {
isLoading: false,
error: action.payload,
data: false
}
}
default: return state;
}
}
export default reducer;
view raw reducers.js hosted with ❤ by GitHub

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.

const getPostById = async (dispatch, id) => {
dispatch({ type: GET_POSTS_BY_ID_REQUEST });
try {
const response = await getPostsById(id);
const res = await response.json();
dispatch({ type: GET_POSTS_BY_ID_SUCCESS, payload: res });
} catch (e) {
dispatch({ type: GET_POSTS_BY_ID_FAIL, payload: e });
}
};
view raw async_func.js hosted with ❤ by GitHub

And a function to give dispatch in the above function’s scope:

export const getPostByIdFunc = dispatch => {
return id => getPostById(dispatch, id);
}

So now our complete actions.js file looks like this:

import {
GET_POSTS_BY_ID_REQUEST,
GET_POSTS_BY_ID_SUCCESS,
GET_POSTS_BY_ID_FAIL,
GET_POSTS_BULK_REQUEST,
GET_POSTS_BULK_SUCCESS,
GET_POSTS_BULK_FAIL
} from "./constants";
import {
getPostsById,
getPostsBulk
} from "./api";
const getPostById = async (dispatch, id) => {
dispatch({ type: GET_POSTS_BY_ID_REQUEST });
try {
const response = await getPostsById(id);
const res = await response.json();
dispatch({ type: GET_POSTS_BY_ID_SUCCESS, payload: res });
} catch (e) {
dispatch({ type: GET_POSTS_BY_ID_FAIL, payload: e });
}
};
const getPostBulk = async dispatch => {
dispatch({ type: GET_POSTS_BULK_REQUEST });
try {
const response = await getPostsBulk();
const res = await response.json();
dispatch({ type: GET_POSTS_BULK_SUCCESS, payload: res });
} catch (e) {
dispatch({ type: GET_POSTS_BULK_FAIL, payload: e });
}
};
export const getPostByIdFunc = dispatch => {
return id => getPostById(dispatch, id);
}
export const getPostsBulkFunc = dispatch => {
return () => getPostBulk(dispatch);
}
view raw actions.js hosted with ❤ by GitHub

Once this is done, all that is left to do is to pass these functions in mapDispatchToProps of our connected component.

const mapDispatchToProps = dispatch => {
return {
getPostById: getPostByIdFunc(dispatch),
getPostBulk: getPostsBulkFunc(dispatch)
}
};

Our App.js file looks like the one below:

import React, { Component } from 'react';
import './App.css';
import { connect } from 'react-redux';
import { getPostByIdFunc, getPostsBulkFunc } from './actions';
class App extends Component {
render() {
console.log(this.props);
return (
<div className="App">
<button onClick={() => {
this.props.getPostById(1);
}}>By Id</button>
<button onClick={() => {
this.props.getPostBulk();
}}>In bulk</button>
</div>
);
}
}
const mapStateToProps = state => {
return {
state
};
}
const mapDispatchToProps = dispatch => {
return {
getPostById: getPostByIdFunc(dispatch),
getPostBulk: getPostsBulkFunc(dispatch)
}
};
view raw App.js hosted with ❤ by GitHub

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.