React Native获取API数据时meals对象undefined的TypeError问题
React Native中useFetch Hook无法正确解构TheMealDB API返回的meals对象问题
问题现象
使用自定义useFetch Hook获取TheMealDB API数据时,控制台输出meals为undefined,触发Uncaught TypeError: Cannot read properties of undefined (reading 'strMealThumb')错误;同时存在组件卸载后更新状态的警告。其他页面使用相同Hook可正常运行,当前页面异常。
问题原因
- 初始状态不匹配:
useFetch中data初始值设为数组[],但TheMealDB的lookup接口返回的是包含meals数组的对象(结构为{ meals: [...] })。组件初始渲染时,解构data.meals会得到undefined,直接访问其属性就会报错。 - 路由参数获取错误:直接将
route.params(对象)作为id拼接进URL,导致API请求的id无效,返回的meals可能为空或不存在。 - Hook依赖缺失:
useFetch的useEffect依赖数组为空,当URL(即路由参数id)变化时,不会重新发起请求。 - 未处理组件卸载后的请求:请求未完成时组件卸载,调用
setData/setLoading会触发状态更新警告。
解决方案
1. 修正useFetch Hook
- 将
data初始值改为对象{},匹配API返回结构 - 给
useEffect添加url依赖,确保URL变化时重新请求 - 加入axios请求取消逻辑,避免组件卸载后更新状态
修改后的useFetch.js:
import React, { useEffect, useState } from "react"; import axios from "axios"; const useFetch = (url) => { const [data, setData] = useState({}); const [loading, setLoading] = useState(true); const [error, setError] = useState(false); useEffect(() => { const source = axios.CancelToken.source(); const fetchData = async () => { try { const response = await axios.get(url, { cancelToken: source.token }); setData(response.data); setLoading(false); } catch (err) { if (!axios.isCancel(err)) { setLoading(false); setError(err.message); } } }; fetchData(); return () => { source.cancel("组件已卸载,取消请求"); }; }, [url]); return { data, loading, error }; }; export default useFetch;
2. 修正DetailMeal组件
- 正确解构路由参数中的
id - 添加条件渲染,在
loading、error或meals不存在时显示对应内容 - 注意TheMealDB返回的
meals是数组,需取第一个元素
修改后的detailMeal.js:
import React from "react"; import { View, Text, ActivityIndicator } from "react-native"; import useFetch from "../../hooks/useFetch/useFetch"; import Style from "./detailMeal.stil"; import DetailCard from "../../components/detailCard/detailCard"; const DetailMeal = ({ route }) => { const { id } = route.params; const { data: { meals }, loading, error } = useFetch(`https://www.themealdb.com/api/json/v1/1/lookup.php?i=${id}`); if (loading) { return <View style={Style.container}><ActivityIndicator size="large" /></View>; } if (error || !meals?.length) { return <View style={Style.container}><Text>加载失败或无数据</Text></View>; } const meal = meals[0]; return ( <View style={Style.container}> <DetailCard imageURL={meal.strMealThumb} title={meal.strMeal} desc={meal.strInstructions} /> </View> ); }; export default DetailMeal;
关键说明
- 条件渲染是避免
undefined属性访问的核心,必须确保meals存在且有数据后,再渲染DetailCard - 路由参数的获取需匹配传递方式,若通过
navigation.navigate('DetailMeal', { id: 123 })传递,必须解构route.params.id - 请求取消逻辑解决了组件卸载后更新状态的警告,提升了应用稳定性
内容的提问来源于stack exchange,提问作者alleySam
相关产品推荐
相关产品推荐

