React: Initializing state with multiple async calls

January 12, 2018

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 :-)


Thank you for reading :-)