Codementor Events

React: Initializing state with multiple async calls

Published Sep 08, 2018Last updated Mar 07, 2019
React: Initializing state with multiple async calls

Working with an organization and helping them migrate their jQuery based app to React. The old version is a monolith created in Django comprising of static HTML for individual pages with Jquery sprinkled throughout. The migration is being done in small steps with injecting React in individual pages and creating an app per page.

Keeping it simple, the overall app structure designed to have 1 container component and multiple presentational components. App state gets initialized in the container component and parts of it are passed as props to respective presentational components.

More on container and presentational components

App structure

The problem: Multiple async calls initializing the app state randomly.

Following is the state of in the product detail page.

    this.state = {
        product_detail: {},
        comments: []
    }

As the app is completely front-end based, all the AJAX calls start in componentDidMount() life cycle method of the component. Individual methods make AJAX calls to get respective part of the state and set the data in the state.

    getCommentsData () {
        this.makeAjaxCall(`https://reqres.in/api/users`, `GET`)
            .then(data => {
                this.setState({comments: data})
            });
    }

    getProductData () {
        this.makeAjaxCall(`https://reqres.in/api/users/2`, `GET`)
            .then(data => {
                this.setState({product_detail: data})
            });
    }

    componentDidMount () {
        this.getCommentsData();
        this.getProductData();
    }

This resulted in the state getting initialized multiple times at multiple places and I had no idea which AJAX call would finish first.

Add to that, setting the state multiple times re-rendered the app giving it sort of a blinky effect everytime an AJAX call finished.

The realization: Initialization of the state has to happen only once and only at one place.

Its called initial state for a reason (duh!). State should not be initialized multiple number of times and should be not be initialized randomly at random places.

The solution: Promise.all to the rescue

With the realization the solution was pretty straightforward. I needed to find the way to wait for all the AJAX calls to finish and set the state once only when all the AJAX calls finished.

Say hello to Promise.all

The change was fairly simple, instead of completing AJAX calls in individual methods I need to return a Promise from them

    getProductData () {
        return this.makeAjaxCall(`https://reqres.in/api/users/2`, `GET`);
    }

    getCommentsData () {
        return this.makeAjaxCall(`https://reqres.in/api/users`, `GET`);
    }

And in componentDidMount() use Promise.all() to wait for the individual Promises to finish and then truly intialize the state.

    componentDidMount () {
        Promise.all([this.getProductData(), this.getCommentsData()])
            .then(([product_detail, comments])  => {
                this.setState({
                    product_detail,
                    comments
                });
            });
    }

Conclusion: Final working solution šŸ˜ƒ

JS Fiddle

Thank you for reading šŸ˜ƒ

Discover and read more posts from Ejaz
get started
post commentsBe the first to share your opinion
Gene Edwards
4 years ago

I have a similar issue that Iā€™m trying to solve using axios. I have second API call that requires and id from the first api call in react. How can I do something like this in order to use data from both api calls in a component

Ejaz
4 years ago

The idea would be similar but instead of using Promise.all you would use chained promises, something like

return ParentPromise({
    ...some code here...
  }).then(function(parent) {
    //promise2
    return ChildPromise(parent.id)
  }).then(function(response) {
    // set your state here
  });
Show more replies