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

useEffect的返回函数会在组件挂载时执行吗?如何仅在卸载时运行?

现象说明

你遇到的表现是完全正常的,不是useEffect的执行逻辑不符合预期,根源是React 18及以上版本默认开启的**严格模式(Strict Mode)**在开发环境的特殊检测机制:

  • 开发环境下,严格模式会对所有组件做一次「挂载→卸载→重新挂载」的模拟流程,用来提前排查副作用未正确清理导致的内存泄漏、重复订阅等问题。你首次访问PageOne时看到的PageOne打印,就是第一次模拟卸载时触发清理函数打出来的,不是组件真的被路由卸载了。
  • 生产环境打包后不会存在这个双重调用的逻辑,你当前写的空依赖useEffect,其返回的清理函数本身就只会在组件真实卸载的时候执行。
  • 从PageOne跳转到PageTwo时打印的第一条PageOne是PageOne真实路由卸载触发的,后面的PageTwo和你首次进PageOne的打印同理,是严格模式模拟PageTwo卸载打出来的。
实现清理逻辑仅在真实卸载时执行的方法

如果你的目标是让清理逻辑只在组件真正被卸载(比如路由切走、组件被条件渲染移除)时执行,分两种场景处理:

  • 生产环境下你当前的代码已经满足要求,不需要做任何修改。
  • 如果是开发环境下不想被严格模式的模拟卸载触发逻辑,有两种可选方案:
    • 方案1:移除根节点的严格模式包裹
      找到项目入口文件(一般是main.jsx/main.tsx),删掉渲染根组件时的<React.StrictMode>包裹即可:

      // 修改前
      ReactDOM.createRoot(document.getElementById('root')).render(
        <React.StrictMode>
          <App />
        </React.StrictMode>
      )
      
      // 修改后
      ReactDOM.createRoot(document.getElementById('root')).render(
        <App />
      )
      

      不推荐长期关闭严格模式,它能在开发阶段帮你提前暴露很多隐蔽的副作用问题,减少线上bug。

    • 方案2:用ref标记组件真实挂载状态,过滤掉模拟卸载的触发
      不需要关闭严格模式,通过ref判断是否是真实卸载即可,示例代码:

      import { useEffect, useRef } from "react";
      import { Link } from "react-router-dom";
      
      export const PageOne = () => {
        const mountedStatus = useRef(false);
      
        useEffect(() => {
          mountedStatus.current = true;
          return () => {
            // 仅当组件真实完成挂载后触发的卸载,才执行清理逻辑
            if (mountedStatus.current) {
              console.log("PageOne 真实卸载");
              // 此处写你需要在卸载时执行的清理逻辑:比如取消请求、清除定时器、解绑事件等
              mountedStatus.current = false;
            }
          };
        }, []);
      
        return (
          <>
            <h1>PageOne</h1>
            <Link to="/two">Page two</Link>
          </>
        );
      }
      

内容的提问来源于stack exchange,提问作者Miguel Angel GEQ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 17:39:21