Matt Takao
Matt Takao

Reputation: 3006

How to setState after a loop of async functions

The closest answer I could find was this, which didn't help since I need to setState: How do I run a function after using array.map?

I think the answer should be simple, but I'm pretty new to Javascript. I'm trying to move the setState for isLoading to AFTER I've pulled all of the profiles.

componentDidMount() {
    console.log('MatchesScreen init props: ', Object.keys(this.props))

    Object.keys(this.props.profile.profile.matches).map((username) => {
      console.log('match', username)
      url = 'https://xxxxxxx.execute-api.us-west-2.amazonaws.com/prod/profile?username=' + username
      fetch(url, {
        method: 'GET',
      })
      .then((response) => response.json())
      .then((responseJson) => {
        this.setState({
          isLoading: false,
        })
        this.props.addMatch(responseJson)
      })
      .catch((error) =>{
        console.error(error);
      })
    })
  }

I've tried various things like appending a .then() to the map function, but so far no luck.

Upvotes: 1

Views: 1266

Answers (3)

Raymond Mutyaba
Raymond Mutyaba

Reputation: 950

Try wrapping the Object.keys() in a while loop with an if statement at the end.

var profilesMatched = 0;
while(profilesMatched < Object.keys(this.props.profile.profile.matches).length){
    Object.keys(this.props.profile.profile.matches).map((username) => {
      console.log('match', username)
      url = 'https://xxxxxxx.execute-api.us-west-2.amazonaws.com/prod/profile?username=' + username
      fetch(url, { method: 'GET', })
      .then((response) => {response.json()})
      .then((responseJson) => {
        this.props.addMatch(responseJson);
        profilesMatched++;
      })
      .catch((error) => {
        console.error(error);
      });
    });
    if(profilesMatched == Object.keys(this.props.profile.profile.matches).length){
        this.setState({
          isLoading: false,
        })
    }
}

Upvotes: 0

Emilio Venegas
Emilio Venegas

Reputation: 556

You could return each promise inside the .map() function, which would leave you with an array of promises. Once you have that, you can use Promise.all() to wait for all the promises in the array to resolve.

componentDidMount() {
    console.log('MatchesScreen init props: ', Object.keys(this.props))

    // Put all promises into an array
    const promisesArray = Object.keys(this.props.profile.profile.matches).map((username) => {
        console.log('match', username)
        url = 'https://xxxxxxx.execute-api.us-west-2.amazonaws.com/prod/profile?username=' + username;
        // Return each promise
        return fetch(url, {
                method: 'GET',
            })
            .then((response) => response.json())
            .then((responseJson) => {
                this.props.addMatch(responseJson)
            })
            .catch((error) =>{
                console.error(error);
            });
    });

    // Wait for all promises in array to resolve
    Promise.all(promisesArray)
        .then(() => {
            // This will execute once all promises have resolved
            this.setState({
                isLoading: false,
            });
        })
        .catch(e => console.error(e));
}

Upvotes: 3

Abe
Abe

Reputation: 5508

Try using the async/await pattern as follows:

async componentDidMount() {
...
    Object.keys(this.props.profile.profile.matches).map((username) => {
    ...
      await fetch(url, {
      ...

then move your setState method into its own callback, which you can call in your .then() statements following your fetch.

I like this reference: https://alligator.io/js/async-functions/

Upvotes: 0

Related Questions