Reputation: 63
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 fine. This also works when I output
wildPokemon.abilities
. However, when I start getting deeper into my nested objects, that's when things go
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
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