Chowdahhh
Chowdahhh

Reputation: 159

Why can I access nested objects before setting my state but not after?

I'm creating a Pokedex app using React as a practice exercise as I've just been learning it, and I'm hitting a bit of a weird snag. So the basic set up so far is I have a PokemonList component that is basically the parent component for a bunch of individual PokemonPreview components. It's in creating these PokemonPreview components and grabbing information from the pokeapi that I'm having some issues. Here's my example code from my PokemonPreview component (just the relevant bits):

const [pokemonInfo, setPokemonInfo] = useState({})

const name = props.pokemon.name.charAt(0).toUpperCase() + props.pokemon.name.slice(1)
const url = props.pokemon.url

useEffect(() => {
    fetch(url)
        .then(res => res.json())
        .then(data => {
            console.log(data.types[0].type.name)
            setPokemonInfo(data)
        })
}, [])

return (
    <div style={{backgroundColor: '#F06430'}} className="pokemon-preview">
        <h3>{name}</h3>
        <h5>#{pokemonInfo.id}</h5>
        <h5>{pokemonInfo.types[0].type.name}</h5>
    </div>
)

The name and url are both passed as props from the PokemonList component, and the url is then used to pull the full details for the pokemon from the pokeapi (ex: https://pokeapi.co/api/v2/pokemon/6). Basically, I'm doing the fetch request to get the data for the pokemon, and saving that whole object as my state, so I can pull whatever info from that object in state as I need it. The weird thing that I'm seeing is that I can't pull some info from my state object. Here is an example of the JSON that is stored in my state object:

{
  "abilities": [
    {
      "ability": {
        "name": "blaze",
        "url": "https://pokeapi.co/api/v2/ability/66/"
      },
      "is_hidden": false,
      "slot": 1
    },
    {
      "ability": {
        "name": "solar-power",
        "url": "https://pokeapi.co/api/v2/ability/94/"
      },
      "is_hidden": true,
      "slot": 3
    }
  ],
  "base_experience": 240,
  "types": [
    {
      "slot": 1,
      "type": {
        "name": "fire",
        "url": "https://pokeapi.co/api/v2/type/10/"
      }
    },
    {
      "slot": 2,
      "type": {
        "name": "flying",
        "url": "https://pokeapi.co/api/v2/type/3/"
      }
    }
  ],
  "weight": 905
}

What I'm trying to access are the entries in the types array. As you can see in the console log in my example code, I'm able to grab the first entry in the types array from the request data, but if I try to get that same info from my state using a console log or in the component's return, I get an error: TypeError: Cannot read property '0' of undefined. The pokemonInfo.id part in the return works but not the more nested types part. I really can't figure out why I'm able to get this info before setting it to my state, but not after. Does anyone know why this is happening? Should I just create separate state variables for each piece of the pokemon's information, instead of storing it in one big object to pull out later?

Thanks!

Upvotes: 0

Views: 105

Answers (1)

Jayce444
Jayce444

Reputation: 9063

Ok so the reason for the error is you're trying to render the info before it's loaded, since the pokemon info is fetched from an API. You have to accommodate for that. Here's a basic technique:

// Set it to null initially, since it's not defined at all
const [pokemonInfo, setPokemonInfo] = useState(null);

...

// Check if the pokemon info is null before trying to access nested data
return (
    <div style={{backgroundColor: '#F06430'}} className="pokemon-preview">
        <h3>{name}</h3>
        {
            pokemonInfo === null ?
            <h5>Loading pokemon data...</h5> :
            <>
                <h5>#{pokemonInfo.id}</h5>
                <h5>{pokemonInfo.types[0].type.name}</h5>
            </>
        }
    </div>
)

And of course make sure the object paths are all correct

Upvotes: 1

Related Questions