React调用API加载数据时出现多次重复请求问题
React 接口重复调用、控制台累计打印6次日志问题排查&修复
核心原因
- React 18+ 开发环境默认开启的严格模式(Strict Mode)是首要触发因素。严格模式为了提前暴露不规范的副作用写法,会在开发环境对所有组件执行「挂载→卸载→二次挂载」的双执行流程,直接让组件渲染、副作用执行次数翻倍,这个机制仅在开发环境生效,生产环境不会触发。
- 自定义Hook的useEffect依赖配置不合规。你写的
useGetProducts中useEffect传入了空依赖数组[],但effect内部实际使用了入参API;同时组件中调用Hook时传入的是模板字符串实时拼接的地址`${API}?limit=5&offset=1`,每次组件渲染都会生成新的字符串引用,空依赖写法会导致effect无法正确感知参数变化,配合严格模式的双挂载、状态更新触发的重渲染,日志和请求次数会持续叠加。 - 重渲染链路叠加计数:组件首次挂载时
products初始值为空数组,触发第一次日志打印;effect执行完成后setProducts更新状态,触发第二次渲染打印日志;严格模式下组件卸载重挂载,会完整复现一遍「空值初始渲染→effect拉取数据→状态更新重渲染」的流程,再叠加父组件重渲染、路由初始化带来的挂载动作,很容易累计到6次日志输出。
修复方案
- 补全useEffect依赖,增加请求清理逻辑避免竞态
修改useGetProducts的代码,不要用空数组逃避依赖检查,同时增加请求取消逻辑,组件卸载或参数变化时取消未完成的旧请求:
const useGetProducts = (API) => { const [products, setProducts] = useState([]) useEffect(() => { const controller = new AbortController(); const fetchData = async () => { try { const response = await axios(API, { signal: controller.signal }); setProducts(response.data) } catch (err) { // 主动取消的请求不需要抛错 if (err.name === 'CanceledError') return; console.error('产品列表拉取失败:', err); } } fetchData(); // 清理函数:组件卸载/依赖变化时取消未完成的请求 return () => controller.abort(); }, [API]); // 将用到的API参数加入依赖数组 return products; }
- 固定API参数引用,避免无意义的重复触发
如果请求参数是固定值,直接把拼接好的API地址提到组件外部作为常量;如果参数是动态值,用useMemo缓存拼接结果,避免每次渲染生成新的字符串引用触发effect重跑:
// 固定参数场景:直接提到组件外作为常量 const PRODUCT_LIST_API = `${API}?limit=5&offset=1`; const ProductList = () => { // 动态参数场景用useMemo缓存: // const productListApi = useMemo(() => `${API}?limit=${limit}&offset=${offset}`, [API, limit, offset]) const products = useGetProducts(PRODUCT_LIST_API); console.log("products: ", products); return ( <section> <div> {products.map((product) => ( <ProductItem product={product} key={product.id} /> ))} </div> </section> ) }
- 不要为了避免重复调用关闭严格模式。严格模式的双调用机制只会在开发环境出现,本质是帮你提前排查副作用未清理、内存泄漏类的问题,只要做好请求清理逻辑,开发环境的重复请求不会影响生产环境的实际运行表现。
内容的提问来源于stack exchange,提问作者alejandro
相关产品推荐
相关产品推荐

