如何修复React中Uncaught TypeError: Cannot read properties of undefined (reading 'front_default')错误
解决React中Uncaught TypeError: Cannot read properties of undefined (reading 'front_default')的问题
我来帮你拆解这个问题,你遇到的错误本质是在异步数据还未完全加载完成时,就尝试访问深层嵌套的属性,而首次加载正常、刷新报错的差异,是组件状态初始化和异步请求的时序问题导致的。
问题根源
先聚焦你的子组件PokemonContainer的核心问题:
- 初始时你把
urlData设为[](空数组),然后通过useEffect监听urlData的变化,只要它变化就将loaded设为true。 - 组件挂载时,
urlData的初始值[]会直接触发这个监听useEffect,把loaded设为true——但这时候请求Pokemon详情的异步请求还没完成,urlData还是空数组,根本不存在sprites属性,自然会抛出读取front_default的错误。 - 首次加载正常是因为父组件的列表请求速度较慢,子组件的详情请求刚好在
loaded被设为true前完成了;但刷新时浏览器可能缓存了父组件的请求,子组件的loaded状态提前变为true,而详情请求还没返回,就触发了报错。
另外父组件的loaded逻辑也有小瑕疵:初始pokemonData是空数组,会触发监听pokemonData的useEffect,直接把loaded设为true,这时候页面会先渲染空列表,等请求完成后才会渲染子组件,虽然不影响功能,但可以优化。
修复方案
方案1:优化子组件状态逻辑(推荐)
修改子组件的状态初始化和加载判断逻辑,确保只有拿到有效数据后才渲染内容:
import React, { useState, useEffect } from 'react'; import axios from 'axios'; export const PokemonContainer = (props: any) => { // 用null作为初始状态,更贴合"数据未加载"的语义 const [urlData, setURLData] = useState<any>(null); useEffect(() => { const get_url_data = async () => { const response = await axios.get(props.url); setURLData(response.data); }; get_url_data(); }, [props.url]); // 必须添加props.url作为依赖,避免props更新时不重新请求 // 直接通过urlData是否存在判断加载状态,无需额外的loaded变量 if (!urlData) { return ( <div> <p>Loading...</p> </div> ) } return ( <div key={urlData.name}> <p>{urlData.name}</p> <p>{urlData.id}</p> {/* 用可选链操作符做安全判断,避免嵌套属性不存在时报错 */} <img src={urlData.sprites?.front_default} alt={urlData.name} /> </div> ) };
方案2:快速修复——添加可选链判断
如果不想修改状态逻辑,直接在访问嵌套属性时使用可选链?.,就能避免数据未加载完成时的报错:
<img src={urlData?.sprites?.front_default}></img>
方案3:优化父组件的加载逻辑
父组件可以简化loaded的触发时机,避免提前渲染空列表:
import './App.css'; import React, { useEffect, useState } from 'react'; import axios from 'axios'; import { PokemonContainer } from './components/Pokemon'; function App() { const [pokemonData, setPokemonData] = useState([]); const [loaded, setLoaded] = useState(false); const get_pokemon_data = async () => { const response = await axios.get('https://pokeapi.co/api/v2/pokemon?limit=9'); setPokemonData(response.data.results); // 请求完成后直接标记为已加载,无需额外的useEffect setLoaded(true); }; useEffect(() => { get_pokemon_data(); }, []); if (loaded) { return ( <div> {pokemonData.map((pokemon: any) => ( <PokemonContainer key={pokemon.name} url={pokemon.url} /> ))} </div> ); } else { return ( <div> <p>wait a minute</p> </div> ) } }; export default App;
关键要点总结
- 避免用空数组
[]作为异步数据的初始状态,用null或undefined更能清晰表达"数据未加载"的状态。 - 监听状态变化时,要确保只有拿到有效数据后才切换到已加载状态,不要被初始状态的变化误触发。
- 访问嵌套对象属性时,优先使用可选链
?.做安全判断,这是React中处理异步数据的常用技巧。 - 组件的
useEffect依赖数组要完整,比如子组件必须把props.url加入依赖,防止props更新时不重新请求数据。
内容的提问来源于stack exchange,提问作者MikeyNeedHelp
相关产品推荐
相关产品推荐

