Async Redux

Uriel Rodriguez
5 min readNov 1, 2020

Continuing from my two previous blogs on Redux, Understanding Redux, and Implementing Redux in React, we have an understanding of Redux’s workflow and how its implemented within a React application. However, up to this point, we have only seen how synchronous tasks are carried out. So we need a way to handle IO operations like accessing a database or an API and luckily there is a library that helps with this. The library is called “Redux Thunk” and it provides access to middleware which injects into the Redux workflow. Middleware are functions that are injected in between established processes, to execute their logic, and only afterward allowing the continuation of the process as normal. So to give an example, the basic workflow for Redux is to have a dispatch function send off an action object which is then received by the reducer function which uses the information in the action to update the global state accordingly. With middleware, and in the case of Redux Thunk, instead of dispatching the action object, the dispatch function dispatches and executes the thunk middleware. The middleware, which again is a function, has access to the dispatch function which it can use to execute its own dispatches to the reducer. After the middleware executes its logic, it then dispatches the action which is received by the reducer as normal. As you can see, the middleware interjects between the normal process of Redux, and in this way asynchronous code is handled. Essentially, the middleware halts the process until some asynchronous task is resolved and then resumes the process.

To illustrate this and implementation we begin by installing the thunk library:

npm install --save redux-thunk

Afterward, the Redux global state needs additional configuration to incorporate the middleware. Within the index.js file, where Redux is imported and configured the “applyMiddleware” function will also be imported along with the “createStore” function, which allows the creation of the global state and the use of middleware within the workflow.

// index.jsimport { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from // some file path
const store = createStore(rootReducer, applyMiddleware(thunk));

With this setup out of the way, we turn our attention to writing the logic that will be executed by the dispatch function. Ordinarily, the dispatch function would send out the action object to the reducer but this time it will execute the thunk middleware. Since the workflow’s complexity is increasing and no longer will the action be dispatched directly, it is a good idea to house all the action logic within their own file. So:

// actions/actions.js// thunk middleware
export const someFunc = () => {
return dispatch => {
getData(dispatch);
};
};
const getData = async(dispatch) => {
try {
const response = await fetch(url);
const data = await response.json();

dispatch({ type: 'SOME_ACTION', data });
} catch (error) {
dispatch({ type: 'ERROR_ACTION', error });
}
};

Lets break down the code above. First we have the thunk middleware which is a function “someFunc” that returns an anonymous function that has access to the dispatch function as an argument. The returned function can then pass the dispatch function as an argument to an action creator which is a function that returns an action object, in this case “asyncFunc”. Next we define the action creator as an asynchronous function that receives the dispatch function and performs an asynchronous task. After resolving the asynchronous task, it then continues the Redux workflow of dispatching the actual action object for the reducer to update the global state with. And as you can see, depending on the result of the Promise, either the fetched data will be passed as a payload or an error.

The example above also mimics a common situation where a component, upon mounting, needs to retrieve data from an API and store it in the global state. Once the asynchronous action creator retrieves or fails to retrieve the data, it dispatches the result as a payload where it will be stored within the global state. To further illustrate this:

// someComponent.jsimport React, { Component } from 'react';
import { connect } from 'react-redux';
import someFunc from // file path to actions.js
class someComponent extends Component {
componentDidMount() {
this.props.onInitData();
}
render() {
return <div>{this.props.someData}</div>;
}
}
const mapStateToProps = state => {
return {
someData: state.data
};
};
const mapDispatchToProps = dispatch => {
return {
onInitData: () => dispatch(someFunc())
};
};
export connect(mapStateToProps, mapDispatchToProps)(someComponent);

So now putting everything together, the component, now connected or subscribed to the global state has access to the information stored in the global state and the dispatch functions that will allow it to trigger an update event via the two functions mapStateToProps and mapDispatchToProps. In addition, the middleware is imported and then passed an an argument to the dispatch function instead of the traditional action object. Upon this component mounting, it will execute the “onInitData” function which was arbitrarily named, more importantly it maps to the dispatch function which executes the middleware.

In following this pattern, asynchronous code can be made to conform to Redux’s synchronous and uniform workflow. In addition to the dispatch function, the thunk middleware also provides access to the global state via the “getState” function. This function can be used to access any data stored within the global state which might be needed when updating the global state in some specific manner.

// actions/actions.js// thunk middleware
export const someFunc = () => {
return dispatch, getState => {
const { someProp } = getState();
getData(dispatch, someProp);
};
};

In this example, you can see the getState function which is used to access some property within the global state via destructuring. That data can then be passed down to the action creator to be used in some manner, ultimately in updating the state.

To learn more about the thunk library and handling asynchronous code, you can review the docs. Also be sure to review my previous articles to recap Redux.

--

--

Uriel Rodriguez

Flatiron School alumni and Full Stack web developer.