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

React组件useEffect内函数未执行,加入依赖数组触发无限循环求助

问题原因与修复方案

核心问题梳理

1. 函数执行逻辑错误

你当前把getAssets和calcValue并行放在依赖为空数组的useEffect中,只会在组件首次挂载时执行一次,且存在时序问题:

  • getAssets是异步请求,调用后立刻执行calcValue时,setRequestedAssets还未完成状态更新,requestedAssets还是初始的空数组,所以calcValue遍历空数组不会得到任何结果,自然看起来没有执行。

2. 无限循环的原因

如果你把两个函数移到useEffect外部再放入依赖数组,会出现无限循环:

  • 组件每次渲染都会重新生成新的函数引用,useEffect检测到依赖的函数引用变化就会重新执行,useEffect内部的setState又会触发组件重渲染,形成死循环。

3. 关于await的奇怪现象

你观察到的改await后热更新有数据、刷新就为空的情况和await本身无关,属于开发环境热重载(HMR)的特性:

  • response.data.market_data.current_price.eur是普通数值不是Promise,加不加await对结果没有任何影响。
  • 改代码保存触发热重载时,会保留组件之前的状态,之前请求到的requestedAssets还存在,所以calcValue能正常计算出结果;刷新浏览器后组件完全重新挂载,状态重置为空,就回到没有数据的情况,只是和你改await的操作巧合撞上了。

修复方案

你的逻辑本身存在先后依赖:需要先拉取到资产列表,再计算每个资产的价值,所以拆分两个useEffect分别处理即可:

function PortfolioComponent() {
  const [requestedAssets, setRequestedAssets] = useState([]);
  const [assets, setAssets] = useState([]);

  // 第一个useEffect:组件挂载时拉取资产列表
  useEffect(() => {
    async function getAssets() {
      try {
        const response = await axios.get("http://localhost:4200/assets");
        const assetResponse = response.data.rows;
        setRequestedAssets(assetResponse);
      } catch (error) {
        console.log(error?.response?.data?.error || error);
      }
    }
    getAssets();
  }, []);

  // 第二个useEffect:资产列表更新时重新计算价值
  useEffect(() => {
    // 资产列表为空时跳过执行
    if (requestedAssets.length === 0) return;

    async function calcValue() {
      try {
        const newAssetArray = await Promise.all(
          requestedAssets.map(async function (asset) {
            const response = await axios.get(assetData(asset.AssetId));
            const cp = response.data.market_data.current_price.eur;
            const value = Number(cp) * Number(asset.Amount);
            return { ...asset, value: value, price: cp };
          })
        );
        setAssets(newAssetArray);
      } catch (error) {
        console.log(error?.response?.data?.error || error);
      }
    }

    calcValue();
  }, [requestedAssets]);

  // 其余组件逻辑
}

如果确实有每次渲染都要执行请求的需求(极不推荐,会导致接口请求泛滥),可以直接把两个函数调用写在组件顶层,不要包裹useEffect。如果是需要手动触发刷新,可以单独把逻辑提取成用useCallback包裹的函数,绑定到按钮点击事件上。

如果后续需要把函数放入useEffect依赖避免循环,可以用useCallback包裹函数,固定函数引用:

const calcValue = useCallback(async () => {
  // calcValue的逻辑
}, [requestedAssets]); // 函数内部用到的外部变量都要放入依赖

useEffect(() => {
  calcValue();
}, [calcValue]); // 此时依赖的calcValue引用不会频繁变化,不会触发无限循环

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 10:45:04