0

I am working on a PokeDex project using the PokeAPI, and could use your help. While trying to handle the json i receive from the API, I am faced with two seemingly distinct array types:

Console representation of arrays

Opened array

For example, I am able to retrieve the names and urls of the first type of array, but unable and/or unsure of how to for example retrieve the type value for bulbasaur.

I think it has something to do with the different way I am filling the arrays, but not sure.

Here is my code:

class App extends Component {
constructor() {
  super();

  this.state = {
    pokemonArray: [],
    pokemonInfoArray: [],
    searchfield: '',
  }
}

componentDidMount() {
  this.fetchKantoDex()
}
// Fetches the pokemon that reside in Kanto.
fetchKantoDex = () => {
  fetch('https://pokeapi.co/api/v2/pokemon?limit=10')
    .then(response => response.json())
    .then(data => this.setState({ pokemonArray: data.results}))
    .then(this.fetchSinglePokemon)
}

// Is called by other fetch methods. Loops through and fetches the information pertaining to
// each pokeon fetched, and stores their info in a seperate array.
fetchSinglePokemon = () => {
  let tempInfo = [];
  for(let i = 0; i < this.state.pokemonArray.length;i++){
    let url = this.state.pokemonArray[i].url;
      fetch(url)
      .then(response => response.json())
      .then(pokedata => tempInfo.push(pokedata))
      .then(pokedata => console.log(pokedata.results))

  } 
  this.setState({ pokemonInfoArray: tempInfo})
  // console.log(this.state.pokemonArray)
  console.log(this.state.pokemonInfoArray)
}
Turtleneck
  • 105
  • 1
  • 9
  • it looks like the second one was mutated later (edit: there it is, tempInfo.push, it was an empty array when logged, but it was mutated later so when you open it you see the lemnts inside) – user120242 Jun 10 '20 at 22:59
  • `tempInfo.push` will not happen before `this.setState` is done (which means you will have some very weird data "inconsistencies". `fetch` is async, `setState`is async (so the `console.log` where you have it now in `fetchSinglePokemon` might be confusing as well depending on when you look at it – Icepickle Jun 10 '20 at 23:00
  • 1
    Does this answer your question? [Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference](https://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron) – user120242 Jun 10 '20 at 23:02
  • 1
    @user120242 the [`fetchKantoDex = () => `](https://babeljs.io/docs/en/babel-plugin-proposal-class-properties) is a class property which is an experimental syntax in babel – Icepickle Jun 10 '20 at 23:08
  • Thank you for your answers. Is there a way to make push happen before setstate? – Turtleneck Jun 10 '20 at 23:28
  • @MariusZ doesn't my answer show you how to do that? – Icepickle Jun 10 '20 at 23:37

2 Answers2

0

Well, I would suggest you first change the signature of the fetchSinglePokemon to include the data array you wish to retrieve, like

fetchSinglePokemon = ( results ) => { /* rest below */ }

Which means, that you would need to change how the fetch inside fetchKantoDex works, like for example so:

fetchKantoDex = () => {
  fetch('https://pokeapi.co/api/v2/pokemon?limit=10')
    .then(response => response.json())
    .then(data => this.fetchSinglePokemon( data.results ) )
}

and then you can change the fetchSinglePokemon like:

fetchSinglePokemon = ( results ) => {
  return Promise.all( 
    results
      .map( ({ url }) => fetch( url ).then( resp => response.json() )
      .then( alldata => this.setState({ pokemonInfoArray: alldata }, () => console.log( this.state.pokemonInfoArray ) ) );
}
Icepickle
  • 12,689
  • 3
  • 34
  • 48
0

Demo of how to implement. There were a few other problems that had to be fixed with your Promise callbacks, and if you want it to try to render objects immediately after they are fetched, you can use then and add them to the state array immediately or use async/await and a for await of loop:

const {Component} = React
const {render} = ReactDOM

class App extends Component {
  constructor() {
    super();

    this.state = {
      pokemonArray: [],
      pokemonInfoArray: [],
      searchfield: '',
    }
  }

  componentDidMount() {
    this.fetchKantoDex()
  }
  // Fetches the pokemon that reside in Kanto.
  fetchKantoDex = () => {
    fetch('https://pokeapi.co/api/v2/pokemon?limit=1')
      .then(response => response.json())
      .then(data => this.setState({
        pokemonArray: data.results
      },this.fetchSinglePokemon))
      // use setState callback, guarantees state is updated before calling fetchSingle, although what you really should do is build all the data before using setState, unless you need state to be updated like this for your components
  }

  // Is called by other fetch methods. Loops through and fetches the information pertaining to
  // each pokeon fetched, and stores their info in a seperate array.
  fetchSinglePokemon = () => {
    let tempInfo = [], fetchArray = [];
    console.log(this.state.pokemonArray)
    for (let i = 0; i < this.state.pokemonArray.length; i++) {
      let url = this.state.pokemonArray[i].url;
      fetchArray.push(
       fetch(url)
        .then(response => response.json())
        .then(pokedata => {
          //tempInfo.push(pokedata)
          return pokedata
          // modified to resolve Promise with tempInfo as value
        })
      )
    }
    //for(const info of await fetchArray)
    Promise.all(fetchArray).then(infoArray => {console.log(infoArray)
      this.setState({
        pokemonInfoArray: infoArray
      })
    })
  }
  
  render() {
    return this.state.pokemonInfoArray.map((info,i) => <div key={i}>{JSON.stringify(info)}</div>)
  }
}


render(<App/>,document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="app"></div>
user120242
  • 14,918
  • 3
  • 38
  • 52