1

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;
Ender91
  • 61
  • 1
  • 8
  • https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call Not too familiar with react, but you should probably use `useEffect` to change the DOM values once the data has loaded. – SuperStormer Mar 13 '21 at 23:36
  • Which exact line throws the error? – David Mar 13 '21 at 23:37

3 Answers3

1

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

Tiago Coelho
  • 5,023
  • 10
  • 17
1

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>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
1

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();
}
cron8765
  • 11
  • 3