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

如何在Promise循环中更新React状态?文件上传场景实践

问题分析与解决方案

问题根源

  1. 闭包捕获旧状态:uploadItems执行时,items是初始空数组,forEach里所有Promise回调都捕获了这个旧值。每次调用setItems(items.concat(itm))都是基于空数组添加单个元素,最终UI只会保留最后完成的上传项。
  2. 并行执行违反WebDav限制:原代码用forEach同时触发所有上传请求,既不符合WebDav逐个上传的要求,也加剧了状态覆盖问题。

修复方案

1. 解决状态更新的闭包问题

使用React函数式状态更新,它会接收当前最新的状态值,彻底避免闭包捕获旧状态的问题:

setItems(prevItems => prevItems.concat(itm));

2. 实现串行上传(符合WebDav要求)

如果需要严格逐个上传(上一个完成再发起下一个),将forEach替换为for...of循环,配合await实现串行执行:

修改后的uploadItems函数:

async function uploadItems() {
  for (const item of data) {
    const uploadedItem = await doUpload(item.id);
    setItems(prev => prev.concat(uploadedItem));
    // 如需额外延迟,可在此添加 await delay();
  }
}

完整修复代码

const data = [
  { id: 1, name: "Marry" },
  { id: 2, name: "had" },
  { id: 3, name: "a" },
  { id: 4, name: "little" },
  { id: 5, name: "lamb" },
];

export default function App() {
  const [items, setItems] = useState([]);

  return (
    <div className="App">
      <button onClick={uploadItems}>Upload items</button>
      <ul>
          {items.map((itm) => (
            <li key={itm.id}>{itm.name}</li> {/* 添加key符合React列表渲染规范 */}
          ))}
        </ul>
    </div>
  );

  async function uploadItems() {
    // 串行逐个发起上传请求
    for (const item of data) {
      const uploadedItem = await doUpload(item.id);
      // 基于最新状态更新列表
      setItems(prevItems => prevItems.concat(uploadedItem));
    }
  }
}

function doUpload(id) {
  const timeout = Math.round(Math.random() * 1000);
  return new Promise((resolve) =>
    setTimeout(() => resolve(data.find((itm) => itm.id === id)), timeout)
  );
}

function delay() {
  return new Promise((resolve) => setTimeout(resolve, 0));
}

额外说明

  • 函数式状态更新是React处理异步状态更新的标准方案,能有效规避闭包带来的旧状态问题。
  • 串行上传既满足WebDav的限制,也能让UI按上传顺序逐步更新,不会出现状态覆盖。
  • 给列表项添加key属性,符合React列表渲染规范,避免不必要的重渲染和控制台警告。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 14:05:17