Search code examples
javascriptreactjses6-promisefetch-api

Promise all and correctly update the state using React.js


Fetching data from API I'm getting such data:

0: {name: "bulbasaur", url: "https://pokeapi.co/api/v2/pokemon-species/1/"}
1: {name: "ivysaur", url: "https://pokeapi.co/api/v2/pokemon- species/2/"}
2: {name: "venusaur", url: "https://pokeapi.co/api/v2/pokemon-species/3/"}
3: {name: "charmander", url: "https://pokeapi.co/api/v2/pokemon-species/4/"}
4: {name: "charmeleon", url: "https://pokeapi.co/api/v2/pokemon-species/5/"}
5: {name: "charizard", url: "https://pokeapi.co/api/v2/pokemon-species/6/"}

In order to get more detailed information I need to fetch each url. I'm trying to do it with Promise.all and update state, but code doesn't work:

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      pokemons: [],
      names: [],
      isLoaded: false
    };
  }
  componentDidMount() {
    this.loadPokemons();
    this.loadPokemonAdditionalInfo();
  }
  loadPokemons() {
    fetch('https://pokeapi.co/api/v2/pokemon-species')
      .then(res => res.json())
      .then(json => {
        this.setState({
          isLoaded: true,
          names: json.results
        })
      })
  }
  loadPokemonAdditionalInfo() {
    Promise.all(this.state.names.map(name =>
      fetch(name.url)
      .then(results => results.forEach(result => result.json())
        .then(r => {
          this.setState({
            pokemons: [...this.state.pokemons, r]
          })
        }))))
  }
}

Please advise what I'm doing wrong.


Solution

  • Firstly, when you attempt to load the additional data, the names won't have finished.

    Just make your componentDidMount function async and await each call (also make sure you're returning a promise for each one).

    async componentDidMount(){
      await this.loadPokemons();
      await this.loadPokemonAdditionalInfo();
    }
    

    Also you are returning the iteration of the results, not an array of each result's json.

    Replace results.forEach(result => result.json()) with:

    results.map(result => result.json()) in loadPokemonAdditionalInfo


    const results = [{ name:'John' }, { name:'Jane' }];
    
    console.log('forEach', results.forEach(result => result.name));
    console.log('map', results.map(result => result.name));