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
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
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
The idea would be similar but instead of using
Promise.all
you would use chained promises, something like