Ender91
Ender91

Reputation: 71

Trying to create a pokemon search using API but running into issue where data is not being loaded on time

I'm trying to create a pokemon search app using the pokemon api here - https://pokeapi.co/ I am getting an error of "Cannot read property 'species' of undefined". Is this where I should be using useEffect? I am guessing that it has to do with me not using an async / await functionality but I wanted to ask if maybe it's something else first.

Here is my code

import { useState, useEffect } from "react";
import "./App.css";
function App() {
const [pokemonName, setPokemonName] = useState("ditto");
const [chosen, setChosen] = useState(false);
const [pokemonData, setPokemonData] = useState({
name: "",
species: "",
img: "",
hp: "",
attack: "",
defense: "",
type: "",
});

const searchPokemon = () => {
const response = fetch(
  `https://pokeapi.co/api/v2/pokemon/${pokemonName}`
).then((response) => {
  setPokemonData({
    name: pokemonName,
    species: response.data.species.name,
    img: response.data.sprites.front_default,
    hp: response.data.stats[0].base.stat,
    attack: response.data.stats[1].base.stat,
    defense: response.data.stats[3].base.stat,
    type: response.data.types[0].type.name,
  });
  setChosen(true);
});
console.log(response);
};

return (
<div className="App">
  <input
    className="border-b-2 border-black px-4"
    type="text"
    onChange={(e) => {
      setPokemonName(e.target.value);
    }}
  />
  <button
    className="rounded text-white bg-blue-500 p-2 text-sm"
    onClick={searchPokemon}
  >
    Search Pokemon
  </button>

  <div>
    {!chosen ? (
      <h1>Please choose a pokemon</h1>
    ) : (
      <>
        <h1>{pokemonData.name}</h1>
        <img src={pokemonData.img} alt={pokemonData.name} />
        <h2>{pokemonData.species}</h2>
        <h2>{pokemonData.type}</h2>
        <h2>{pokemonData.hp}</h2>
        <h2>{pokemonData.attack}</h2>
        <h2>{pokemonData.defense}</h2>
      </>
    )}
  </div>
</div>
);
}

export default App;

Upvotes: 1

Views: 4090

Answers (3)

cron8765
cron8765

Reputation: 11

Try creating a function to fetch the data from the API. Something like this

function getPokemonData() {
    fetch('https://pokeapi.co/api/v2/pokemon/')
        .then((response) => response.json())
        .then((response) =>
            setPokemonData({
                name: pokemonName,
                species: response.data.species.name,
                img: response.data.sprites.front_default,
                hp: response.data.stats[0].base.stat,
                attack: response.data.stats[1].base.stat,
                defense: response.data.stats[3].base.stat,
                type: response.data.types[0].type.name,
            })
        );
}

And then call this function inside componentDidMount

componentDidMount(){
   this.getPokemonData();
}

Upvotes: 1

Ori Drori
Ori Drori

Reputation: 192607

You don't convert the response to .json(), and you also add .data when parsing response, which is not needed. In addition, you base.stat should be base_state.

const { useState, useEffect } = React;

function App() {
  const [pokemonName, setPokemonName] = useState("ditto");
  const [chosen, setChosen] = useState(false);
  const [pokemonData, setPokemonData] = useState({
    name: "",
    species: "",
    img: "",
    hp: "",
    attack: "",
    defense: "",
    type: "",
  });

  const searchPokemon = () => {
    const response = fetch(
    `https://pokeapi.co/api/v2/pokemon/${pokemonName}`
    )
    .then(response => response.json())
    .then(response => {
      setPokemonData({
        name: pokemonName,
        species: response.species.name,
        img: response.sprites.front_default,
        hp: response.stats[0].base_stat,
        attack: response.stats[1].base_stat,
        defense: response.stats[3].base_stat,
        type: response.types[0].type.name,
      });
      setChosen(true);
    });
  };
  
  return (
    <div className="App">
      <input
        className="border-b-2 border-black px-4"
        type="text"
        onChange={(e) => {
          setPokemonName(e.target.value);
        }}
      />
      <button
        className="rounded text-white bg-blue-500 p-2 text-sm"
        onClick={searchPokemon}
      >
        Search Pokemon
      </button>

      <div>
        {!chosen ? (
          <h1>Please choose a pokemon</h1>
        ) : (
          <React.Fragment>
            <h1>{pokemonData.name}</h1>
            <img src={pokemonData.img} alt={pokemonData.name} />
            <h2>{pokemonData.species}</h2>
            <h2>{pokemonData.type}</h2>
            <h2>{pokemonData.hp}</h2>
            <h2>{pokemonData.attack}</h2>
            <h2>{pokemonData.defense}</h2>
          </React.Fragment>
        )}
      </div>
    </div>
  );
}

ReactDOM.render(
  <App />,
  root
);
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>

<div id="root"></div>

Upvotes: 1

Tiago Coelho
Tiago Coelho

Reputation: 5101

you forgot to do response.json() after the fetch, to get the body in json format, and you have a few errors in the data shape (the response is not under a "data" attribute)

see here a modified working version: https://codesandbox.io/s/keen-brook-8egfe?file=/src/App.js

Upvotes: 1

Related Questions