Reputation: 101
I managed to fix one of the things that was spotted in my previous post. Which was to switch the brackets on a useState
declaration. But I ran into another problem, it seems to be related to how my code is laid out within the useEffect
. Should I delete it? Or should I shift my code around to allow the pkmnResponse
to be created and loaded? Before it was inside of a try/catch, and the axios.get
call had an await attached to it before I took out based on some digging in StackOverflow. I noticed when I just saved my code and it refreshed on its own, I was able to console.log stats from it, but it still gave me this error:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading '0') at getPkmnStats (PkmnStats.js:62:1)
The page loads without any data, just a broken image link and blank attributes.
Here is the code for PkmnStats:
import axios from 'axios';
import { useParams } from 'react-router-dom';
import loading from './loading.gif';
function PkmnStats() {
const [isLoading, setIsLoading] = useState(true);
const [pkmnResponse, setPkmnResponse] = useState();
const [pkmnStat, setPkmnStat] = useState(
{
name:'',
index:'',
imageUrl:'',
types:[''],
description:'',
stats:{
hp:'',
attack:'',
defense:'',
speed:'',
specialAttack:'',
specialDefense:''
},
height:'',
weight:'',
abilities:['']
}
);
const params = useParams()
const index = params.index
const name = params.name
const pkmnUrl = `https://pokeapi.co/api/v2/pokemon/${index}`
const pkmnSpecies = `https://pokeapi.co/api/v2/pokemon-species/${index}/`
const imageUrl = `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${index}.png`
useEffect(() => {
const getPkmnStats = async () => {
axios.get(pkmnUrl).then((response) => {
setIsLoading(true);
setPkmnResponse(response.data)
//const species = pkmnResponse.data.species;
}).catch(error => {
setIsLoading(false);
console.log("An error happened", error);
});
console.log( "heres the response data " + pkmnResponse.stats[0].base_stat)
const hp = pkmnResponse.stats[0].base_stat
const attack = pkmnResponse.stats[1].base_stat
const defense = pkmnResponse.stats[2].base_stat
const speed = pkmnResponse.stats[5].base_stat
const specialAttack = pkmnResponse.stats[3].base_stat
const specialDefense = pkmnResponse.stats[4].base_stat
// pkmnResponse.data.stats.map((stat) => {
// if(stat.stat.name === 'hp'){
// hp = stat['base_stat'];
// }
// if(stat.stat.name === 'attack'){
// attack = stat['base_stat'];
// }
// if(stat.stat.name === 'defense'){
// defense = stat['base_stat'];
// }
// if(stat.stat.name === 'speed'){
// speed = stat['base_stat'];
// }
// if(stat.stat.name === 'special-attack'){
// specialAttack = stat['base_stat'];
// }
// if(stat.stat.name === 'special-defense'){
// specialDefense = stat['base_stat'];
// }
// });
const height = Math.round((pkmnResponse.data.height * 0.328084 + 0.0001) * 100)/100;
const weight = Math.round((pkmnResponse.data.weight * 0.220462 + 0.0001) * 100)/100;
const types = pkmnResponse.data.types.map(types => {return types.type.name});
const abilities = pkmnResponse.data.abilities.map(abilities => {
return abilities.ability.name
});
// getPkmnDescription(pkmnSpecies);
let description ='';
const getDesc = axios.get(pkmnSpecies).then(() => {
getDesc.data.flavor_text_entries.some(flavor => {
if (flavor.language.name === 'en') {
description = flavor.flavor_text;
return description;
}
});
}).catch(error => {
setIsLoading(false);
console.log("An error happened", error);
});
setPkmnStat({ index, name, imageUrl, types, stats: {hp, attack, defense, speed, specialAttack, specialDefense}, height, weight, abilities, description})
}
getPkmnStats();
},[])
return (
<>
{isLoading ? (
<div className='mx-auto w-fit h-screen p-6 md:w-3/5'>
<div className='flex flex-col items-center justify-center md:flex-col lg:flex-row'>
<div className='flex flex-col items-center '>
<p className='text-5xl md:text-6xl'>{params.name}</p>
<img className=" w-32 h-32 md:w-64 md:h-64 " src={pkmnStat.imageUrl} alt={pkmnStat.name} />
<div className='flex flex-row '>
<div>{pkmnStat.types[0] ? pkmnStat.types[0] : null}</div>
<div>{pkmnStat.types[1] ? pkmnStat.types[1] :null}</div>
</div>
<p>Height: {pkmnStat.height} ft.</p>
<p>Weight: {pkmnStat.weight} lbs.</p>
</div>
<div className='flex flex-col items-center '>
<p className='text-center my-5'>{pkmnStat.description} </p>
<p>Abilities: {pkmnStat.abilities}</p>
<div className='flex flex-col items-center mt-4 '>
<p> HP: {pkmnStat.stats.hp}</p>
<p> Attack: {pkmnStat.stats.attack}</p>
<p>Defense: {pkmnStat.stats.defense}</p>
<p>Speed: {pkmnStat.stats.speed}</p>
<p>Spec.Attack: {pkmnStat.stats.specialAttack}</p>
<p>Spec.Defense: {pkmnStat.stats.specialDefense}</p>
</div>
</div>
</div>
</div>
) : ( <img src={loading} className="mx-auto bg-platnium" alt="loading gif"/>) }
</>
)
}
export default PkmnStats
PkmnCard and App for reference:
PkmnCard
import loading from './loading.gif'
import {Link} from 'react-router-dom'
const PkmnCard = ({pokemon}) => {
const index = pokemon.url.split('/')[pokemon.url.split('/').length - 2];
const sprite =`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${index}.png`;
return (
<Link to={`/pokemon/${pokemon.name}/${index}` }>
{pokemon ? (<div className='flex flex-col h-64 items-center p-10 rounded-xl shadow-lg hover:shadow-2xl hover:border-stone-700 bg-white select-none '>
<h3 className='text-center capitalize'>{pokemon.name}</h3>
{pokemon.imageLoading ? (<img src={loading} className="mx-auto w-10 h-10" alt="loading gif"/>) :null}
<img className='mx-auto' style={pokemon.tooManyRequests ? {display: "none"} : pokemon.imageLoading ? null : {display: "block"}} src={sprite} alt={pokemon.name} onLoad={() => ({imageLoading: false})} onError={ () => ({ tooManyRequests: true})} />
{pokemon.tooManyRequests ? (<h6 className='mx-auto bg-red-600'> <span className='text-white'>Too Many Requests </span></h6>) :null }
<h3 className="text-center">-No.{index}-</h3>
</div>): (<img src={loading} className="mx-auto bg-platnium" alt="loading gif"/>)}
</Link>
)
}
export default PkmnCard
App
import React from 'react'
import { Route, BrowserRouter, Routes} from 'react-router-dom'
import Navbar from './components/Navbar'
import PkmnList from './components/PkmnList'
import PkmnStats from './components/PkmnStats'
function App (){
return (
<BrowserRouter>
<div className="w-full">
<Navbar/>
<Routes>
<Route path="/" element={<PkmnList/>} />
<Route path="/pokemon/:name/:index" element={<PkmnStats/>} />
</Routes>
</div>
</BrowserRouter>
)
}
export default App;
Any help with understanding what I did wrong and how/why to something else is appreciated!
Upvotes: 0
Views: 134
Reputation: 768
To quickly solve your problem, use useEffects's
dependency array to listen for state changes in pkmResponse
. Also, there's no reason to call getPkmStats()
in useEffect
twice - remove that last call. useEffect
will call it on initial mount, so you are good there. Your useEffect
block should look like this:
useEffect(() => {
},[pkmnResponse])
This will now give you access to pkmnResponse
.
Also, here is a little light refactoring that helps readability. Instead of throwing everything in useEffect
and cluttering it's block, I would modularize the fetch function and the pkmStat
setters like this:
useEffect(() => {
getPkmnStats()
},[pkmnResponse])
const getPkmnStats = async () => {
axios.get(pkmnUrl).then((response) => {
setIsLoading(true);
setPkmnResponse(response.data)
pkmnStatSetter()
//const species = pkmnResponse.data.species;
}).catch(error => {
setIsLoading(false);
console.log("An error happened", error);
});
}
const pkmStatSetter = () => {
const hp = pkmnResponse.stats[0].base_stat
const attack = pkmnResponse.stats[1].base_stat
const defense = pkmnResponse.stats[2].base_stat
const speed = pkmnResponse.stats[5].base_stat
const specialAttack = pkmnResponse.stats[3].base_stat
const specialDefense = pkmnResponse.stats[4].base_stat
const height = Math.round((pkmnResponse.data.height * 0.328084 + 0.0001) * 100)/100;
const weight = Math.round((pkmnResponse.data.weight * 0.220462 + 0.0001) * 100)/100;
const types = pkmnResponse.data.types.map(types => {return types.type.name});
const abilities = pkmnResponse.data.abilities.map(abilities => {
return abilities.ability.name
});
// getPkmnDescription(pkmnSpecies);
let description ='';
const getDesc = axios.get(pkmnSpecies).then(() => {
getDesc.data.flavor_text_entries.some(flavor => {
if (flavor.language.name === 'en') {
description = flavor.flavor_text;
return description;
}
});
}).catch(error => {
setIsLoading(false);
console.log("An error happened", error);
});
setPkmnStat({ index, name, imageUrl, types, stats: {hp, attack, defense, speed, specialAttack, specialDefense}, height, weight, abilities, description})
}
Upvotes: 2