Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for multiple async calls to finish before React render

I have a component, which relies on data to be asynchronously retrieved before the content is rendered. If the data is not yet available, the render function will return a Loader component instead:

    if (this.state.loading) {
        return <Loader />;
    }

The state of loading is set to false after the call returns data:

componentDidMount() {

        ExternalComponent.fetchData().then(response => {
            this.setState({
                loading: false,
                data: response
            });
        });
}

This works ok, but what if I wanted to add in another async fetch call in parallel? How would I properly wait for both to finish before setting the state of 'loading' to false?

like image 517
Rolodecks Avatar asked Sep 05 '18 14:09

Rolodecks


2 Answers

Using Promise.all:

componentDidMount() {
  const fetchData1 = ExternalComponent.fetchData()
  const fetchData2 = AnotherExternalComponent.fetchData()

  Promise.all([ fetchData1, fetchData2 ]).then((responses) => {
      this.setState({
          loading: false,
          data: responses[0]
      });
  });
}
like image 167
bozdoz Avatar answered Oct 06 '22 00:10

bozdoz


You have more than one option here I think but if you already use redux why don't you move your logic there? With the help of redux-thunk you can do your asynchronous operations in your action creators and use a global process state.

Related reducer:

const initialState = 0;

const progressReducer = (state = initialState, action) => {
  switch (action.type) {
    case types.INCREMENT_PROGRESS:
      return state + 1;
    case types.DECREMENT_PROGRESS:
      return Math.max(state - 1, 0);
    default:
      return state;
  }
};

Related actions:

export const incrementProgress = () => ({ type: types.INCREMENT_PROGRESS });
export const decrementProgress = () => ({ type: types.DECREMENT_PROGRESS });

Then your action creators would be something like this:

export const anAsyncFunction = () => async dispatch => {
  dispatch(incrementProgress());
  try {
    const someResponse = await someAsyncJob();
    dispatch(anAction(someResponse));
  } catch (error) {
    errorHandler(error);
  } finally {
    dispatch(decrementProgress());
  }
};

This is an example of async functions but you can use promises of course. When you want to make multiple async operations just fire your action creators and they increment the process by one. In your component, you will check if your progress state is greater than 0 or like if(progress) shortly than show a loading component.

like image 28
devserkan Avatar answered Oct 05 '22 23:10

devserkan