如何构建axios.get以避免‘Cannot read properties of undefined (reading '0')’错误
问题分析与解决方法
错误原因
- 异步请求顺序错误:axios.get是异步操作,你在then回调外直接访问
pkmnResponse,此时请求还未完成,pkmnResponse仍是初始的undefined,读取pkmnResponse.stats[0]自然报错。 - setState异步特性误解:
setPkmnResponse是异步更新状态的,即使在then里调用,也无法立刻拿到新的状态值,必须直接使用then回调里的response.data处理数据。 - 第二个异步请求未正确等待:获取描述的axios请求也是异步的,你直接在后面调用
setPkmnStat,此时description还未被赋值,会是空字符串。 - isLoading逻辑颠倒:你的return逻辑是
isLoading为true时显示内容,false时显示加载,但初始isLoading设为true,页面加载时直接渲染未填充数据的内容,导致显示空白和破碎图片。
解决方法
- 将所有依赖请求结果的代码放到axios的回调内部,或使用
async/await简化异步逻辑,确保数据返回后再处理。 - 直接使用请求返回的
response.data,而非依赖setState后的状态值。 - 等待两个接口请求都完成后再更新
pkmnStat,用Promise.all同时发起两个请求,提升效率。 - 修正isLoading逻辑:初始设为true,请求完成(成功/失败)后设为false,return时判断
!isLoading再显示内容,否则显示加载动画。
修正后的PkmnStats代码
import axios from 'axios'; import { useState, useEffect } from 'react'; import { useParams } from 'react-router-dom'; import loading from './loading.gif'; function PkmnStats() { const [isLoading, setIsLoading] = useState(true); 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 () => { try { // 并行发起两个请求,等待全部完成 const [pkmnRes, speciesRes] = await Promise.all([ axios.get(pkmnUrl), axios.get(pkmnSpecies) ]); const pkmnData = pkmnRes.data; const speciesData = speciesRes.data; // 提取基础数值 const hp = pkmnData.stats[0].base_stat; const attack = pkmnData.stats[1].base_stat; const defense = pkmnData.stats[2].base_stat; const speed = pkmnData.stats[5].base_stat; const specialAttack = pkmnData.stats[3].base_stat; const specialDefense = pkmnData.stats[4].base_stat; // 转换单位 const height = Math.round((pkmnData.height * 0.328084 + 0.0001) * 100) / 100; const weight = Math.round((pkmnData.weight * 0.220462 + 0.0001) * 100) / 100; // 提取类型与特性 const types = pkmnData.types.map(type => type.type.name); const abilities = pkmnData.abilities.map(ability => ability.ability.name); // 提取英文描述 let description = ''; speciesData.flavor_text_entries.some(flavor => { if (flavor.language.name === 'en') { description = flavor.flavor_text; return true; } return false; }); // 更新状态 setPkmnStat({ index, name, imageUrl, types, stats: { hp, attack, defense, speed, specialAttack, specialDefense }, height, weight, abilities, description }); } catch (error) { console.log("请求出错", error); } finally { // 无论请求成功失败,结束加载状态 setIsLoading(false); } }; getPkmnStats(); }, [index, name]); // 参数变化时重新发起请求 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 capitalize'>{name}</p> <img className=" w-32 h-32 md:w-64 md:h-64 " src={imageUrl} alt={name} /> <div className='flex flex-row '> {types.map((type, idx) => ( <div key={idx} className='mx-2 capitalize'>{type}</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.map(ability => ( <span key={ability} className='mx-1 capitalize'>{ability}</span> ))}</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
额外优化点
- 补充了
useState和useEffect的导入,原代码遗漏导致运行错误。 - 优化了类型、特性的渲染逻辑,用循环替代手动索引判断,适配多类型宝可梦。
- 给useEffect添加依赖项,确保路由参数变化时自动重新请求对应数据。
- 用
async/await+Promise.all简化异步代码,同时并行请求提升加载速度。
内容的提问来源于stack exchange,提问作者Brad G.
相关产品推荐
相关产品推荐

