Async calls can be tricky in Redux because of how payloads work. They modify state off of what is returned and not what will be returned. Because of this, developers become confused with how they should place their API calls inside of action creators. If you’re interested in seeing the full code or taking a look at the course, click here.

Async Await Calls

When creating an HTTP service, the underlying technology that is used are ajax calls. You usually a library like axios or a native API such as fetch to create your HTTP request and return a JSON payload to your application. One issue that I’ve commonly seen is developers having issues with their UI’s not updating as expected even though they see their API call on the network. The reason for this is usually a user returns a promise and not a resolved promise(the payload from the API call). This is the difference between what is actually returned vs what should be returned. One of the ways to fix this is using async await in your API calls. You specify that a function is async, then place await in the return to make sure that a ‘resolved’ promise is returned.

Here is an example of a simple api call and its action creator.

export const loadTodos = () => {
    return fetch(baseUrl)
        .then(res => res.json());
    
};
export const getAllTodos = (dispatch) => dispatch({
        type: ACTION_TYPES.FETCH_TODO_LIST, 
        payload: loadTodos()
});

The main issue with this is your result is a promise. The promise gets returned instead of the actual JSON data. This is a lot obvious when you use Typescript or a Visual Studio Code because it will indicate the type inside of the payload.

Promise Action Creator

Promise Action Creator

Then you’ll try to get that todolist as props inside of your React Component

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { loadTodos, createTodo, deleteTodo } from '../../services/todoService';
import TodoItem from './TodoItem';
import { ListGroup, ListGroupItem, Button } from 'reactstrap';
import { getAllTodos } from './todoReducer';

export class TodoList extends Component {
    constructor() {
        super();

    }

    componentDidMount() {
        this.props.getAllTodos();
    }
    render() {
        const { todos, match, loading, todoList } = this.props;
        console.log(this.props);
        return (
            <div>
                {
                    loading && (
                        <p><em>Loading...</em></p>
                    )}
                {
                    !loading && (
                        <div>
                            <h1>All Todos</h1>
                            <a className="btn btn-primary btn-lg btn-block active" role="button" aria-pressed="true" href={'/todos/create'}>Create</a>
                            <ListGroup>
                                {todoList.todos.map((todo) =>
                                    <TodoItem key={todo.id} todo={todo} />
                                )
                                }
                            </ListGroup>

                        </div>
                    )
                }
            </div>
        )
    }
}

const mapStateToProps = (state, ownProps) => ({
    todos: [],
    todoList: state.todoReducer
});

const mapDispatchToProps = dispatch => ({
    getAllTodos: () => dispatch(getAllTodos)
});

export default connect(
    mapStateToProps, 
    mapDispatchToProps
)(TodoList);

Now if you try to run this, you may see that your Todolist doesn’t always populate.

Empty React Component

Empty React Component

If you investigate, you’ll still see the GET request.

TodoList Load Fetch Http Request

TodoList Load Fetch Http Request

Now we have to figure out how to fix this.

Async Await HTTP Service

This issue can be fixed easily. Let’s start off by taking the service and make it an async await function.

export const loadTodos = async () => {
    return await fetch(baseUrl)
        .then(res => res.json());
    
};

Then after that, let’s go to the action creator and make that async.

export const getAllTodos = async (dispatch) => dispatch({
        type: ACTION_TYPES.FETCH_TODO_LIST, 
        payload: loadTodos()
});

Or if you’re using a package like redux-action’ you can simplify your code like this.

export const getAllTodos = createAction('FETCH_TODO_LIST', async () => loadTodos );

That is all you have to do.

React Redux TodoList

React Redux TodoList

Conclusion

Building out React Redux applications that can connect to external services requires that you properly handle the asynchronous nature of HTTP requests. Making sure that data is populated first before dispatching to your store is a key component of updating your React components consistently. If you’re interested in seeing the full code or taking a look at the course, click here.

Codebrains Newsletter

Get weekly dev news and tutorials.

Powered by ConvertKit