本文详解如何使用 useeffect 配合空依赖数组,确保 react 函数组件中的初始化 api 请求仅执行一次,防止因状态更新导致的无限重渲染与请求爆炸问题。
本文详解如何使用 useeffect 配合空依赖数组,确保 react 函数组件中的初始化 api 请求仅执行一次,防止因状态更新导致的无限重渲染与请求爆炸问题。
在 React 函数组件中,直接在组件顶层(render body)发起网络请求是严重错误实践——每次状态更新(如 setProgress)都会触发组件重新执行,从而反复调用 axios.get,形成恶性循环,最终耗尽浏览器资源甚至导致崩溃。
你原代码的问题核心在于:
- axios.get(...).then(...) 被写在组件函数体内部,而非副作用管理机制中;
- 循环中频繁调用 setProgress(progress + 1) 触发多次重渲染,每次重渲染都重新执行整个 App 函数,包括初始请求;
- 缺乏依赖控制,无法保证副作用“只运行一次”。
✅ 正确解法:使用 useEffect 并传入空依赖数组 [],将其声明为“仅在组件挂载时执行一次”的副作用:
使用 @ainative/react-sdk 为 React 应用添加 AI 聊天和积分。适用于 (1) 安装 @ainative/react-sdk,(2) 使用 useChat hook 实现聊天完成。
import './App.css';
import axios from 'axios';
import React, { useState, useEffect } from 'react';
function App() {
const [allDataJson, setAllData] = useState(null);
const [progress, setProgress] = useState(0);
useEffect(() => {
// ✅ 此逻辑仅在组件首次挂载时执行一次
const fetchAllPokemon = async () => {
try {
// 第一步:获取全部 Pokémon 列表(分页拉取)
let allResults = [];
let url = "https://pokeapi.co/api/v2/pokemon/?offset=0&limit=20";
while (url) {
const response = await axios.get(url);
allResults = [...allResults, ...response.data.results];
url = response.data.next; // 自动处理分页,避免 do-while + await 混用风险
}
// 第二步:并发获取详情(推荐 Promise.all 提升性能)
const detailPromises = allResults.map(async (item) => ({
...item,
detail: await getDetails(item.url)
}));
// 使用 Promise.all 并行加载,避免串行阻塞 + 多次 setState
const fullData = await Promise.all(detailPromises);
setAllData(fullData); // ✅ 一次性设置最终数据
} catch (error) {
console.error("Failed to load Pokémon data:", error);
}
};
fetchAllPokemon();
}, []); // ? 空数组 = 无依赖,仅挂载时执行
return (
<div>
{allDataJson ? (
<div>
<h2>✅ 加载完成!共 {allDataJson.length} 只宝可梦</h2>
<pre>{JSON.stringify(allDataJson.slice(0, 2), null, 2)}</pre>
</div>
) : (
<div>⏳ 正在加载中... 当前进度:{progress}/20</div>
)}
</div>
);
}
// ✅ 优化缓存:使用 Map 替代普通对象,更健壮
const cache = new Map();
export async function getDetails(url) {
const id = url.split('/').filter(Boolean).at(-2); // 安全提取 ID
const cacheKey = `pokemon-${id}`;
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const response = await axios.get(url);
cache.set(cacheKey, response.data);
return response.data;
}
export default App;? 关键改进点说明:
- useEffect + [] 是解决“一次性初始化”的标准范式,彻底切断重复请求根源;
- 移除循环内 setProgress:避免高频 setState。若需真实进度条,应使用 Promise.allSettled 或 for await...of + 单次 setProgress 更新;
- Promise.all 并行请求:大幅提升性能(20 个请求 ≈ 1 秒 vs 串行 20+ 秒);
- 缓存健壮性增强:用 Map 和 filter(Boolean).at(-2) 更安全地解析 URL;
- 错误边界处理:包裹 try/catch,防止 Promise 拒绝中断流程。
⚠️ 注意事项:
- 切勿在 useEffect 外部、组件函数体内直接调用异步操作;
- 不要将 progress 用作“实时计数器”再 setState —— 它不是响应式变量,且频繁更新会拖慢 UI;
- 如需复杂进度反馈(如百分比),建议改用 useState 管理 loadingCount + totalCount,并在 Promise.all 的 .then 中统一更新。
遵循此模式,你的数据加载将稳定、高效、可维护,真正符合 React 的设计哲学。

















