8

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?

6
  • Some where in your react data store, you will need to set the loading flag to true. I am assuming that you are using flux or redux model. If you set the flag to true, then the data store will go through return the flag. I think if you want to call two async fetch calls, you would likely have to use promises which is the equivalent of multi-threading in java but it pulls data one at a time. Commented Sep 5, 2018 at 14:24
  • Your question has a react-redux tag, do you use redux? Commented Sep 5, 2018 at 14:29
  • @devserkan Yes I am, would this ideally be handled through Redux? Commented Sep 5, 2018 at 14:30
  • 1
    Take a look at Promise.all! developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Commented Sep 5, 2018 at 14:32
  • 1
    Although you've found the solution I still have provided an answer for Redux side. In the future maybe you remember this :) Commented Sep 5, 2018 at 14:44

3 Answers 3

21

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]
      });
  });
}
Sign up to request clarification or add additional context in comments.

Comments

2

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.

1 Comment

Redux-saga will be better solution. Much leaner
0

To keep things simple you could have two or more loading flags as shown below:

if (this.state.loading1 || this.state.loading2) {
    return <Loader />;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.