在React函数组件中,避免状态更新引发无限重渲染导致请求重复执行,核心方案是使用`useEffect`配合空依赖数组,使异步初始化仅在组件挂载时执行一次,同时结合`Promise.all`批量更新状态与`Map`缓存优化性能。
这篇文章会帮你彻底搞懂 React 函数组件中因状态更新触发无限重渲染,导致 axios 请求反复执行的问题。核心方案是使用 useEffect 配合空依赖数组,让异步初始化逻辑只在组件挂载时执行一次,同时附带进度管理与缓存优化的实战经验。
在 React 函数组件开发中,直接在函数体顶层调用异步请求(例如 axios.get(...))看起来简单直接,但实际会引发严重问题。当组件状态更新时(比如调用 setProgress),组件会重新渲染,而重新渲染又会导致该请求再次执行,形成无限循环,最终耗尽浏览器资源甚至导致页面崩溃。你遇到的代码问题的根源正在于此:axios.get(...).then(...) 没有被约束执行时机,且循环中频繁调用 setProgress(progress + 1) 造成了连续重渲染。
正确的解决方案非常清晰:将数据获取逻辑放入 useEffect Hook,并使用空依赖数组 [] 明确告诉 React——只在组件首次挂载时执行一次,后续状态更新不会再触发此逻辑。
长期稳定更新的攒劲资源: >>>点此立即查看<<<
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 fetchData = 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; // 继续下一页,直到 next 为 null
}
// 第二步:并发获取详情(推荐 Promise.all 提升性能)
const detailPromises = allResults.map(async (item) => ({
...item,
detail: await getDetails(item.url)
}));
// 使用 Promise.all 并行加载,避免串行阻塞
const fullData = await Promise.all(detailPromises);
// 批量更新状态,减少渲染次数
setAllData(JSON.stringify(fullData));
} catch (error) {
console.error("Failed to fetch data:", error);
}
};
fetchData();
}, []); // 空数组确保只运行一次
return (
{allDataJson (
Completed Load
) : (
Loading, please wait... {progress}/20
)}
);
}
// 优化后的缓存细节(修复原 cache 全局污染风险)
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,不要图省事直接写在函数体里。await + setState:每次 setState 都会触发一次重渲染,循环中这样做等于自爆。正确做法是先用 Promise.all() 并行请求,最后统一 setState。[] 表示无依赖,只在挂载/卸载时触发;如果要响应 props 或 state 变化,必须显式列出依赖项,不要遗漏或随意填写。Map 或 WeakMap:比普通对象更安全,能避免原型污染,且支持任意键类型,使用起来更灵活。try/catch,否则一个 Promise 拒绝就能让页面白屏,用户体验会非常差。如果你需要真实的进度条(而不是简单的计数),可以考虑结合 useReducer 来管理加载状态,或者用 React.Suspense + 自定义 Hook 封装异步逻辑。这样不仅能提升代码清晰度,也让可维护性和用户体验再上一个台阶。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述