1

I am fetching data from an API. I am building an array of 5 objects using the API call. What I am trying to do is iterate over the array, use the data inside each array index to build a component and pass along the props to another component.

I've tried accessing the element the same way I normally would by doing: img={pokemon.name} but it keeps returning undefined. When I type in console.log(pokemon) I get the individual pokemon stored within the array of objects.

import React, { Component } from "react";
import Pokecard from "./Pokecard";

async function getPokemon() {
  const randomID = Math.floor(Math.random() * 151) + 1;
  const pokeRes = await fetch(`https://pokeapi.co/api/v2/pokemon/${randomID}/`);
  const pokemonJSON = await pokeRes.json();
  return pokemonJSON;
}

function buildPokemon() {
  let pokemonArr = [];
  let builtPokemon = {};

  getPokemon()
    .then(data => {
      builtPokemon.name = data.forms[0].name;
      builtPokemon.exp = data.base_experience;
      builtPokemon.img = data.sprites.front_default;
      builtPokemon.type = data.types[0].type.name;

      pokemonArr.push(builtPokemon);
    })
    .catch(err => console.log(err));
  return pokemonArr;
}

class Pokedex extends Component {
  constructor(props) {
    super(props);
    this.state = { pokemonArr: [] };
  }
  componentDidMount() {
    const pokemonArr = [];
    for (let i = 0; i < 5; i++) {
      pokemonArr.push(buildPokemon());
    }
    this.setState({ pokemonArr });
  }
  render() {
    console.log(this.state.pokemonArr);
    return (
      <div className="Pokedex">
        {this.state.pokemonArr.map(pokemon => console.log(pokemon))}
      </div>
    );
  }
}

export default Pokedex;

What should happen is that when I map the pokemonArr I want to create 5 separate pokemon by doing this.state.pokemonArr.map(pokemon => <Pokecard name={pokemon.name} but I keep getting undefined whenever I check this.props in the Pokecard component.

I think my buildPokemon() function is working because when I call it in the componentDidMount() and then I console.log this.state.pokemonArr in the render() function, I actually get an array returned with 5 different pokemon with the proper fields filled out.

And also when I map out this.state.pokemonArr.map(pokemon => clg(pokemon)), it actually displays each individual pokemon. When I pass the pokemon item into a component like this <Pokecard name={pokemon}/>, I see all the pokemon data. when I type <Pokecard name={pokemon.name} I get undefined

0

3 Answers 3

0

There are several problems with your approach but the main one is that getPokemon() is asynchronous.

Return the getPokemon() promise from buildPokemon() and return the object from it's then()

In your for() loop create an array of these promises and use Promise.all() to set state once they have all resolved

function buildPokemon() {

  let builtPokemon = {};
  // return the promise
  return getPokemon()
    .then(data => {
      builtPokemon.name = data.forms[0].name;
      builtPokemon.exp = data.base_experience;
      builtPokemon.img = data.sprites.front_default;
      builtPokemon.type = data.types[0].type.name;
      // return the object
      return builtPokemon
    });
}


componentDidMount() {
  const pokemonPromises = [];
  for (let i = 0; i < 5; i++) {
    pokemonPromises.push(buildPokemon());
  }
  Promise.all(pokemonPromises).then(pokemonArr => this.setState({ pokemonArr }));    
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you SO much. This worked. I have a question if you don't mind. If I'm trying to store an array of asynchronous data, I should send back the object as a promise, then call it in my loop and store each promise in an array of promises, and then Promise.all and update the state? Sorry if what I said was confusing. Long-story short I need to be more careful around asynchronous data.
Short answer is you were returning the array before it got populated. And on top of that you were creating an array of arrays. I left that last part out of my explanation to minimize confusion.
So now you have an array of promises with each promise returning a single object. Promise.all() returns an array of those objects once resolved
Thank you again charlie, your explanation was great too. I see exactly what I was doing wrong now. You rock dude.
0

componentDidMount executes after first render, initially your state is pokemonArr: [] (whch is empty) so you are getting an error. You need to conditionally render like,

{this.state.pokemonArr.length > 0 && this.state.pokemonArr.map(pokemon => console.log(pokemon))}

Side Note:

In buildPokemon function you are returning an array, and again in componentDidMount you are storing it in array which creates array of array's, you just need to return object from buildPokemon function.

Comments

0

The problem is mainly how the Promise should be resolved.

The data isn't available right away so the state (pokemonArr) should only be set once data is available.

Here's the refactored component:

class Pokedex extends Component {
  constructor(props) {
    super(props);
    this.state = { pokemonArr: [] };
  }
  componentDidMount() {
    for (let i = 0; i < 5; i++) {
      this.getPokemon()
        .then((pokemon) => this.buildPokemon(pokemon));
    }
  }
  async getPokemon() {
    const randomID = Math.floor(Math.random() * 151) + 1;
    const pokeRes = await fetch(`https://pokeapi.co/api/v2/pokemon/${randomID}/`);

    return pokeRes.json();
  }
  setPokemon(pokemon) {
    this.setState({
      pokemonArr: [
        ...this.state.pokemonArr, pokemon
      ],
    });
  }
  buildPokemon(data) {
    let builtPokemon = {};

    builtPokemon.name = data.forms[0].name;
    builtPokemon.exp = data.base_experience;
    builtPokemon.img = data.sprites.front_default;
    builtPokemon.type = data.types[0].type.name;

    this.setPokemon(builtPokemon);
  }
  render() {
    return (
      <div className="Pokedex">
        {this.state.pokemonArr.map(pokemon => console.log(pokemon))}
      </div>
    );
  }
}

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.