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

React组件中迭代API调用遇渲染问题,求排查与优化方案

问题排查与优化方案

核心问题解析

你的代码存在两个关键问题导致列表不渲染:

  1. 异步请求顺序错误:forEach中的fetch是异步操作,你在所有请求完成前就调用了setItems(fetched),此时fetched还是空数组;后续请求完成后fetched被修改,但不会触发组件重新渲染(因为没有再次调用setItems)。
  2. 数组映射未返回元素:ItemHolder中items.map使用了大括号{}但没有显式返回<li>元素,导致map返回的是全undefined的数组,React不会渲染任何内容。

修复代码

第一步:修复异步请求逻辑

使用Promise.all(或Promise.allSettled)等待所有装备详情请求完成后再更新状态:

import { useState, useEffect } from "react";

const getItems = () => {
  const [items, setItems] = useState([]); // 初始化为空数组而非null
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(true);

  const fetchCall = async () => {
    try {
      // 获取装备索引列表
      const indexResponse = await fetch('https://www.dnd5eapi.co/api/equipment/');
      if (indexResponse.status >= 400) {
        throw new Error("server error");
      }
      const indexData = await indexResponse.json();
      const indexes = indexData.results.map(item => item.index);

      // 并行发起所有装备详情请求
      const itemPromises = indexes.map(index => 
        fetch(`https://www.dnd5eapi.co/api/equipment/${index}`)
          .then(res => {
            if (res.status >=400) throw new Error(`Failed to fetch ${index}`);
            return res.json();
          })
      );

      // 等待所有请求完成
      const itemData = await Promise.all(itemPromises);
      // 提取名称存入状态
      setItems(itemData.map(data => data.name));
    } catch (err) {
      setError(err);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => { fetchCall() }, []); // 空依赖确保仅首次挂载执行

  return { items, error, loading };
};

第二步:修复列表渲染逻辑

确保map函数返回<li>元素(去掉大括号,或添加return):

const ItemHolder = () => {
  const { items, error, loading } = getItems();

  if (error) return <p>A network error was encountered: {error.message}</p>;
  if (loading) return <p>Loading...</p>;

  return (
    <>
      <h1>Items</h1>
      <ul>
        {items.map((item, i) => <li key={i}>{item}</li>)}
        {/* 或者显式return:{items.map((item,i)=>{ return <li key={i}>{item}</li> })} */}
      </ul>
    </>
  );
};

进阶优化建议

1. 使用Promise.allSettled()处理部分请求失败场景

如果希望即使部分装备请求失败,仍能渲染成功获取的装备,可以替换Promise.all为Promise.allSettled:

const itemResults = await Promise.allSettled(itemPromises);
const successfulItems = itemResults
  .filter(result => result.status === 'fulfilled')
  .map(result => result.value.name);
setItems(successfulItems);

2. 确保数据仅首次挂载获取

你的useEffect已经使用了空依赖数组[],这已经能确保fetchCall仅在组件首次挂载时执行一次,无需额外修改。

3. 自定义Item对象的最佳实践

你计划创建包含追踪属性的Item对象并映射API数据的做法是符合最佳实践的,建议:

  • 定义清晰的Item结构,明确需要追踪的属性(如name、description、price、customPrice等)
  • 在获取API数据后,直接映射为自定义Item对象,便于后续维护和状态管理:
// 示例自定义Item映射
const customItems = itemData.map(data => ({
  id: data.index, // 用index作为唯一ID,比数组索引更可靠
  name: data.name,
  description: data.desc?.join(' ') || '无描述',
  originalPrice: data.cost?.quantity || 0,
  customPrice: data.cost?.quantity || 0, // 初始化为原价
  currency: data.cost?.unit || 'gp'
}));
setItems(customItems);

这样后续修改价格时,只需更新customPrice属性即可,状态管理更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 14:47:50