Fitri Rozi
Fitri Rozi

Reputation: 63

Pokemon API Uncaught TypeError: Cannot read property '0' of undefined

I'm trying to access the abilities of my pokemon, but I keep getting the same error. I'm using React hooks to build my project, and the data that I fetched from Pokemon API was set to setWildPokemon. If I put wildPokemon.name, I'll get the name of the pokemon, which is fineimage. This also works when I output wildPokemon.abilities. However, when I start getting deeper into my nested objects, that's when things gohaywire

code description

function App() {
  const [pokedex, setPokedex] = useState([]);
  const [wildPokemon, setWildPokemon] = useState({});
  const [storeCard, setStoreCard] = useState({});

  const { id, sprites } = wildPokemon;
  // console.log(id, sprites.back_shiny);
  console.log(wildPokemon);
  console.log(wildPokemon.name);
  // console.log(wildPokemon.types[0].name);

  useEffect(() => {
    encounterWildPokemon();
  }, []);

  const pokeId = () => {
    const min = Math.ceil(1);
    const max = Math.floor(151);
    return Math.floor(Math.random() * (max - min + 1)) + min;
  };


  const encounterWildPokemon = () => {
    axios
      .get(`https://pokeapi.co/api/v2/pokemon/${pokeId()}`)
      .then(response => {
        setWildPokemon(response.data);
      });
  };

  const catchPokemon = pokemon => {
    setPokedex(state => {
      const monExists = state.filter(p => pokemon.id === p.id).length > 0; // mostly false. Only true if you catch the same pokemon
      if (!monExists) {
        state = [...state, pokemon];
        state.sort(function(a, b) {
          return a.id - b.id;
        });
      }
      return state;
    });
    encounterWildPokemon(); // MISTAKE: we have to call this function whenever we're done
  };

  const releasePokemon = id => {
    setPokedex(state => state.filter(p => p.id != id));
  };

  // PokeModal
  const [show, setShow] = useState(false);

  const handleClose = () => setShow(false);
  const handleShow = pokemon => {
    setShow(true);
    setStoreCard(pokemon);
  };

  // JSX
  return (
    <div className="app-wrapper container">
      <header>
        <h1 className="title">React Hooks</h1>
        {/* <img src="{sprites[0].back_default}" /> */}
        <h3 className="subtitle">With Pokémon</h3>
      </header>
      <section className="wild-pokemon">
        <h2>Wild Encounter</h2>
        <img
          src={
            "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/" +
            wildPokemon.id +
            ".png"
          }
          className="sprite"
          alt=""
        />
        <h3>{wildPokemon.name}</h3>
        <button className="catch-btn" onClick={() => catchPokemon(wildPokemon)}>
          CATCH
        </button>
      </section>

UPDATED: Ok, I just solved the problem =). I was trying to access the data as soon as the webpage renders. Since fetching is asynchronous, I was basically trying to get the data that hasn't existed yet, which is an empty object.

Upvotes: 2

Views: 787

Answers (1)

Clarity
Clarity

Reputation: 10873

The state is an empty object on the initial load, and that's what is used for the first render. When you try to access sprites[0], sprites is undefined, since the data hasn't been loaded yet. one way to solve this issue is to delay the render until the data is fetched:

 return (
  sprites && sprites.length && (
    <div className="app-wrapper container">
      <header>
        <h1 className="title">React Hooks</h1>
        <img src={sprites[0].back_default} />
        <h3 className="subtitle">With Pokémon</h3>
      </header>
   ...    
  )

Alternatively you can use a loading state and set it to true until the data is fetched. Helpful when you want to show a loader meanwhile.

const [loading, setLoading] = useState(true);

const encounterWildPokemon = () => {
    setLoading(true); 
    axios
      .get(`https://pokeapi.co/api/v2/pokemon/${pokeId()}`)
      .then(response => {
        setWildPokemon(response.data);
        setLoading(false);
      });
  };


// JSX
return (
  loading ? <p>Loading...</p> : (
    <div className="app-wrapper container">
      <header>
        <h1 className="title">React Hooks</h1>
        <img src={sprites[0].back_default} />
        <h3 className="subtitle">With Pokémon</h3>
      </header>
   ...    
  )

Upvotes: 1

Related Questions