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

useEffect内async函数的while循环中使用await不生效问题

问题根因
  • 语法错误:原代码中 setProductsList([...productsList,item]]) 多写了一个右中括号,会直接导致语法解析失败。
  • 同步代码优先执行:callProduct 是异步函数,调用后不会阻塞后续同步逻辑,console.log(productsList.length) 运行时异步请求和 state 更新还未执行,拿到的始终是初始值 0。
  • 闭包捕获旧 state:useEffect 依赖项为空数组,内部作用域捕获的 productsList 永远是初始渲染时的空数组,每次执行 setProductsList([...productsList, item]) 都是基于空数组拼接数据,最终只会保留最后一次循环拿到的 item,无法实现数据累加。
  • 错误使用 await 修饰 setState:React 的 state 更新方法不返回 Promise,await setProductsList() 写法无效,不会等待 state 更新完成再执行后续逻辑。
修复方案

推荐先批量请求完所有数据再一次性更新 state,减少不必要的渲染次数,也能从根源避免闭包问题,代码示例如下:

useEffect(() => {
  // 标记组件挂载状态,避免组件卸载后更新state引发内存泄漏
  let isMounted = true;

  async function callProduct(){
    const tempList = [];
    let a = 5;
    while (a > 0) {
      const item = await contract.getProduct(a);
      tempList.push(item);
      a--;
    }
    if (isMounted) {
      setProductsList(tempList);
      // 需查看更新后的数据可在该处打印
      console.log(tempList.length);
    }
  }

  callProduct();

  // 清理函数
  return () => {
    isMounted = false;
  };
}, []);

如果确实需要每次拿到单条数据就立即更新 state,可以使用 setState 的函数式更新写法,每次调用都能拿到最新的 state 值:

useEffect(() => {
  let isMounted = true;

  async function callProduct(){
    let a = 5;
    while (a > 0) {
      const item = await contract.getProduct(a);
      if (isMounted) {
        // 函数式更新取上一次的state值,不受闭包影响
        setProductsList(prevList => [...prevList, item]);
      }
      a--;
    }
  }

  callProduct();

  return () => {
    isMounted = false;
  };
}, []);
额外说明

如果需要在 productsList 更新后执行对应逻辑,可以单独声明 useEffect 监听该 state 变化:

useEffect(() => {
  console.log(productsList.length);
  // 处理更新后的渲染、计算等逻辑
}, [productsList]);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 06:57:01