React+TS中JSX渲染对象数据map报错的最简实现方案
报错根因
.map() 是数组原型上的内置方法,只有数组类型可以直接调用。你抛出listOfCategories.map is not a function错误,说明你存在state里的listOfCategories不是数组——和你描述的“state中存储了一个包含嵌套子对象的对象”完全吻合:你直接把普通对象传入state初始化,却把它当数组执行遍历,必然报错。
最简实现方案
React没有提供直接遍历对象渲染JSX的能力,你只需要先把对象转成可遍历的数组结构,再执行map遍历即可,配合TS类型校验可以从根源避免这类类型错误。
- 首先去掉代码里的
any类型,any会完全关闭TS的类型校验,这类低级错误本可以在编码阶段就被TS提示,不需要等到运行才抛错。 - 如果你的数据源是键值对结构的普通对象,根据遍历需求选择
Object.keys()/Object.values()/Object.entries()把对象转成数组再遍历。 - 给state加兜底初始值,避免父组件异步传参时,首次渲染拿到
undefined触发报错。 - 遍历的
key必须绑定当前项唯一、稳定的字段(比如数据id),不要直接传整个对象当下标,也不要用数组下标当key,避免列表重排时出现渲染异常。 - 如果要渲染嵌套子对象,把单条分类的渲染逻辑抽成可递归的组件,遇到子分类字段递归调用自身即可,不需要写多层重复遍历代码。
修正后的TS实现代码
import { useState } from "react" // 根据业务实际字段定义类型,不要用any interface CategoryItem { id: string | number; name: string; cover?: string; description?: string; children?: CategoryItem[]; } interface CategoriesProps { datas: { categories: Record<string, CategoryItem> } } // 单条分类卡片组件,支持递归渲染子分类 function CategoryCard({ category }: { category: CategoryItem }) { return ( <div key={category.id} className="relative flex flex-wrap h-96 my-20"> <div className="absolute right-0 top-0 h-full"> <img className="rounded-lg max-w-full h-96" src={category.cover || "https://double-you-design.fr/wp-content/uploads/2016/04/dummy-post-horisontal.jpg"} alt={category.name} /> </div> <div className="absolute -bottom-10 bg-white rounded-lg shadow-lg w-7/12 h-48 p-8"> <h3 className="text-xl font-bold">{category.name}</h3> <p className="mt-2 text-gray-600">{category.description}</p> {/* 递归渲染嵌套子分类 */} {category.children?.length ? ( <div className="mt-4 pl-4 border-l"> {category.children.map(sub => <CategoryCard key={sub.id} category={sub} />)} </div> ) : null} </div> </div> ) } export default function Categories(props: CategoriesProps) { // 加兜底值,避免props未加载时取数报错 const [categoryMap] = useState(props.datas?.categories || {}) return ( <div className="container mx-auto"> {/* 普通对象转数组后再遍历 */} {Object.values(categoryMap).map(category => ( <CategoryCard key={category.id} category={category} /> ))} </div> ) }
额外说明
如果你业务预期里categories本身应该是数组类型,那说明父组件传值逻辑有问题,你可以在初始化state时加类型校验兜底,不管上游传什么值都保证state是数组类型,彻底杜绝这类报错:
const initData = Array.isArray(props.datas?.categories) ? props.datas.categories : [] const [listOfCategories, setListOfCategories] = useState<CategoryItem[]>(initData)
内容的提问来源于stack exchange,提问作者Johan
相关产品推荐
相关产品推荐

