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

React函数组件useEffect首次渲染时API重复调用两次解决方法

问题原因

你遇到的API重复调用、日志重复打印问题,是React 18版本后**开发环境默认开启的严格模式(Strict Mode)**的预期行为,不是你写的路由或者组件逻辑有bug。
严格模式下,组件首次挂载流程中React会主动执行一次「挂载→卸载→重新挂载」的操作,目的是提前检测组件副作用的写法问题,比如忘记清理的定时器、事件监听、未做中断的重复请求等隐患,生产环境打包构建后不会出现这个双调用现象。

可行解决方案
  • 方案1:直接关闭严格模式(仅适合临时调试,不推荐长期使用)
    找到项目入口文件(通常是main.jsx/index.js),移除根组件外层的<React.StrictMode>包裹即可:

    // 修改前
    root.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>
    );
    
    // 修改后
    root.render(<App />);
    

    这个方案会丢失严格模式的代码隐患检测能力,长期维护项目不建议这么做。

  • 方案2:给useEffect增加清理逻辑,主动中断重复请求(官方推荐标准写法)
    严格模式下第一次执行effect后,会立刻触发effect的return清理函数,再重新执行effect。你只要在清理函数里加上请求中断逻辑,就能避免无效的重复请求落库、重复处理返回值的问题,同时还能兼容生产环境下组件快速卸载、依赖变更重跑effect的场景,避免内存泄漏。
    修改后的User组件代码示例(基于axios + AbortController实现):

    import React, { useEffect, useState } from "react";
    import axios from "axios";
    // 你自己封装的Api.Get方法也可以扩展支持signal参数
    // import Api from "./api";
    
    export default function User() {
      const [userInfo, setUserInfo] = useState(null);
    
      useEffect(() => {
        const abortController = new AbortController();
    
        const getUser = async () => {
          try {
            // 给请求传入中断信号
            const res = await axios.get("/User", {
              signal: abortController.signal
            });
            // 如果你用自己封装的Api.Get,可以改成下面的写法
            // const res = await Api.Get("User", { signal: abortController.signal });
            if (res.status === 200) {
              console.log(res);
              setUserInfo(res.data);
            }
          } catch (err) {
            // 主动中断的请求会抛出异常,直接过滤即可
            if (axios.isCancel(err) || err.name === "AbortError") return;
            console.error("请求异常:", err);
          }
        };
    
        getUser();
        console.log(111);
    
        // 清理函数:effect重跑/组件卸载前中断未完成的请求
        return () => {
          abortController.abort();
        };
      }, []);
    
      return (
        <>
          <div>Hello</div>
          {userInfo && <div>当前用户:{userInfo.name}</div>}
        </>
      );
    }
    
  • 方案3:使用成熟的请求库自动处理
    如果项目里用SWR、TanStack Query(原React Query)这类数据请求库,它们内部已经默认做了开发环境重复请求去重、请求缓存、无效请求中断的逻辑,直接按照库的文档写请求即可,不需要手动处理严格模式的双调用问题。

避坑提醒:不要用useRef存挂载标记位(比如判断if(!didMount.current)才发请求)的方式规避双调用,这种写法会绕过严格模式的检测,后续如果组件真的因为业务逻辑需要卸载重挂,会出现请求不触发的逻辑bug。

内容的提问来源于stack exchange,提问作者Jack The Baker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 03:12:18