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
相关产品推荐
相关产品推荐

