React.js中map遍历映射项调用异步请求获取分类长度报错如何解决
问题根因
- 你直接在JSX渲染语法中调用了异步
length函数,所有async声明的函数默认返回Promise对象,React不支持直接将Promise作为渲染内容,这就是触发Objects are not valid as a React child报错的核心原因。 - 其次该写法会导致组件每一次重渲染都重复发起接口请求,存在严重的性能问题和内存泄漏风险。
修复方案
推荐两种常用实现方式,按需选择即可:
方案1:批量请求所有分类长度后统一渲染
在组件内新增state存储所有分类对应的长度,在分类列表更新时批量请求数据后再渲染:
// 新增state存储所有分类长度 const [categoryLengths, setCategoryLengths] = useState({}); useEffect(() => { const fetchAllLengths = async () => { const lengthMap = {}; for (const item of categories) { // 调用你原有逻辑获取长度 const len = await length(item.id); lengthMap[item.id] = len; } setCategoryLengths(lengthMap); }; // 仅当分类列表更新时重新请求 fetchAllLengths(); }, [categories]);
渲染时直接从state中取值即可:
{categories.map((x, index) => { return ( <div key={index} className="col-xl-3 col-lg-3 col-md-3 col-6"> <div className="dashboard__categories-item"> <span style={{ color: `${x.color}` }}>{x.icon}</span> <h5>{x.title}</h5> {/* 未加载完成时显示兜底文案 */} <h5>Length: {categoryLengths[x.id] ?? '加载中'}</h5> </div> </div> ); })}
方案2:拆分独立子组件单独管理请求
把每个分类项拆成独立子组件,每个组件内部管理自己的长度请求逻辑,更适合分类数量多的场景:
// 独立分类项组件 const CategoryItem = ({ category }) => { const [len, setLen] = useState(0); const [loading, setLoading] = useState(true); useEffect(() => { const fetchLen = async () => { const res = await length(category.id); setLen(res); setLoading(false); }; fetchLen(); }, [category.id]); return ( <div className="col-xl-3 col-lg-3 col-md-3 col-6"> <div className="dashboard__categories-item"> <span style={{ color: category.color }}>{category.icon}</span> <h5>{category.title}</h5> <h5>Length: {loading ? '加载中' : len}</h5> </div> </div> ); }; // 原父组件内直接使用子组件 {categories.map((x, index) => <CategoryItem key={index} category={x} />)}
额外优化建议
可以给你的请求函数增加错误兜底逻辑,避免接口请求失败导致页面异常:
async function getLength(category_id) { try { const res = await axios.post("/api/passwords/categories", { category_id, user_id, }); return res; } catch (err) { console.error(err); // 接口报错时返回兜底值 return { data: { length: 0 } }; } }
内容的提问来源于stack exchange,提问作者user16473744
相关产品推荐
相关产品推荐

