You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React TypeScript中Context数据存在却无法map,报undefined错误求助

React TypeScript 遍历数组时出现 "undefined 无法执行 map" 错误

问题场景

React TypeScript项目中,访问URL触发Context内的函数拉取数据,数据返回后更新状态并传递给子组件,子组件尝试用map遍历数组时抛出"Cannot read properties of undefined (reading 'map')"错误,但控制台打印能看到数据确实存在。相同逻辑在JavaScript环境下正常运行。

相关代码

CategoryGallery 组件

import React from 'react'
import { IMeal } from '../../_types/contexType'
import styles from './CategoryGallery.module.scss'

interface props {
  meals: IMeal[]
}

function CategoryGallery({meals}:props) {
  if(!meals) return <h1>Loading ...</h1>

  console.log(meals)
  return (
    <div className={styles.gallery}>
      {meals.map((x,i) => {
        const {strMeal,strMealThumb} = x;
        return (
          <figure className={`${styles.gallery__item} ${styles.gallery__item__[i]}`}>
            <img src={strMealThumb} alt={strMeal}/>
            <h6 className={styles.gallery__item__heading}>{strMeal}</h6>
          </figure>
        )
      })}
    </div>
  )
}

export default CategoryGallery

SingleRCategory 组件

import React, { useEffect } from 'react'
import {useParams} from 'react-router-dom';
import CategoryFilter from '../../components/CategoryFiler/CategoryFilter';
import CategoryGallery from '../../components/CategoryGallery/CategoryGallery';
import { useGlobalContext } from '../../context';
import styles from './SingleRCategory.module.scss';

function SingleRCategory() {
  const {categoryname} = useParams<{categoryname?:string}>();
  const {byCategory,category} =  useGlobalContext()
  
  useEffect(() => {
    if(byCategory){
      if(!categoryname) return;
      byCategory(categoryname)
    }
  },[categoryname,category])

  const getCategoryTheme = ()=> {
    const Theme = new Map([
      ["meat","meatTheme"],
      ["vegie","vegieTheme"],
      ["seaFood","seaFoodTheme"],
    ]);
    if(categoryname){
      const selectedItem = Theme.get(categoryname);
      return selectedItem || "";
    }
    return "";
  }
  
  return (
   <div className={`${styles.singleRcCategory} ${styles[getCategoryTheme()]}`}>
      {category &&  <CategoryFilter meals={category}/>}
      {category && <CategoryGallery meals={category}/>}
    </div>
  )
}

export default SingleRCategory

Context Provider 代码

import { createContext, useContext, useEffect, useState } from "react";
import { IMealsContext, IMeal, IntroFood } from "./_types/contexType";

export const AppContext = createContext<Partial<IMealsContext>>({});

export type AppProviderProps = {
    children: React.ReactNode
}

export function AppProvider({children}: AppProviderProps){
    const [category,setCategory] = useState<IMeal[]>();
    const [singleMeal,setSingleMeal] = useState<IMeal>();
    const [favoriteMeals,setFavoriteMeals] = useState<IMeal[]>();
    const [introFood,setIntroFood] = useState<IntroFood[]>();

    const dataFetcher = async (arr: string[]) => {
        const data= await Promise.all(arr.map(async (x) => {
            const meal = await fetch(`https://www.themealdb.com/api/json/v1/1/filter.php?c=${x}`);
            return meal.json();
        }));
        const flattend: IMeal[] = data.flatMap((x) => x.meals);
        return flattend;
    }

    const forIntro = () => {
        fetch("https://www.themealdb.com/api/json/v1/1/search.php?f=h")
        .then(res => res.json())
        .then(data => data.meals)
        .then((data:IMeal[]) => {
            return data.slice(1,3).map(x => {
                const {strMeal,strMealThumb,strIngredient1,strIngredient2,strIngredient3,strIngredient4} = x;
                const rating = Math.ceil(Math.random() * 10).toString();
                const ingredients = [strIngredient1,strIngredient2,strIngredient3,strIngredient4];
                return {
                    img:strMealThumb,
                    name:strMeal,
                    rating,
                    ingredients
                } as IntroFood;
            })
        })
        .then(result => setIntroFood(result))
    }

    const mealMap = new Map([
        ["meat",["Beef","Chicken","Goat","Lamp","Pork"]],
        ["vegie",["Vegan","Vegetarian"]],
        ["seaFood",["Seafood"]],
    ])

    const byCategory =  (category: string) => {
        const selectedCategory = mealMap.get(category);
        if(selectedCategory)
            dataFetcher(selectedCategory).then(res => setCategory(res));
    }

    const forFeature = async () => {
        const letters = ["a","h","d"];
        const data= await Promise.all(letters.map(async (x) => {
            const meal = await fetch(`https://www.themealdb.com/api/json/v1/1/search.php?f=${x}`);
            return meal.json();
        }));
        const flattend: IMeal[] = data.flatMap((x) => x.meals);
        setFavoriteMeals(flattend.slice(2));
    }

    useEffect(() => {
        forIntro()
    },[])

    return <AppContext.Provider value={{category,singleMeal,introFood,byCategory,forFeature,favoriteMeals}}>
        {children}
    </AppContext.Provider>
}

export const useGlobalContext = () => {
    return useContext(AppContext)
}

错误截图

控制台显示存在数据但抛出map错误
组件调用map时meals为undefined

问题根源

  1. 类型与初始状态不匹配:Context中category初始值为undefined,但CategoryGallery的props要求meals是IMeal[]类型。尽管父组件用category && ...做了判断,但TypeScript严格模式下,组件初始化阶段category为undefined,此时props类型不兼容,且运行时可能存在渲染时机差,导致子组件接收到undefined。
  2. useEffect依赖错误:SingleRCategory的useEffect依赖了category,每次category更新都会重新调用byCategory,触发setCategory,造成循环重渲染,状态波动过程中可能传递undefined给子组件。
  3. 接口数据兜底缺失:dataFetcher中未处理接口返回meals为undefined的情况,flatMap处理undefined会导致最终返回数组异常。

解决办法

1. 修正子组件props类型

允许meals为undefined,并在组件内做兜底处理:

interface props {
  meals: IMeal[] | undefined
}

function CategoryGallery({meals}:props) {
  if(!meals) return <h1>Loading ...</h1>
  // 原有逻辑不变
}

或者给props设置默认空数组,避免判断:

interface props {
  meals?: IMeal[]
}

function CategoryGallery({meals = []}:props) {
  return (
    <div className={styles.gallery}>
      {meals.map((x,i) => {
        // 原有逻辑不变
      })}
    </div>
  )
}

2. 修复useEffect依赖项

移除category依赖,避免循环调用:

useEffect(() => {
  if(byCategory && categoryname){
    byCategory(categoryname)
  }
},[categoryname, byCategory])

3. 给Context状态设置初始空数组

将category初始化为空数组,从根源避免undefined:

const [category,setCategory] = useState<IMeal[]>([]);

此时父组件的判断可改为category.length > 0,或者子组件直接处理空数组。

4. 接口数据兜底

在dataFetcher中处理meals为undefined的情况:

const flattend: IMeal[] = data.flatMap((x) => x.meals || []);

内容的提问来源于stack exchange,提问作者irakli777

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 13:07:39