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

在Electron-Vite+React+TypeScript项目中,如何在StrictMode下可靠监听RecipePage路由页面的离开事件?

在Electron-Vite+React+TypeScript项目中,如何在StrictMode下可靠监听RecipePage路由页面的离开事件?

太懂你被StrictMode坑到的烦躁了!开发环境下它会故意重复挂载/卸载组件来检测副作用,直接用useEffect监听location.pathname根本分不清是真的离开页面,还是StrictMode在搞测试,而且onBlur完全不适用——因为路由切换是组件卸载,不是元素失去焦点,自然没效果。

下面给你两个靠谱的解决方案,按需选择就行:

方案一:全局路由监听+追踪上一个路径

这个思路是在路由根节点加一个全局监听组件,对比当前路由和上一个路由,精准判断是不是从/create页面离开的,完美避开StrictMode的干扰。

实现步骤:

  1. 先写一个全局路由监听组件,用useLocation追踪路由变化,同时用状态存上一个路径:
import { useLocation, useEffect, useState } from 'react';

const RouteChangeListener = () => {
  const location = useLocation();
  const [prevPathname, setPrevPathname] = useState(location.pathname);

  useEffect(() => {
    // 当路由变化时,先判断是不是从/create离开的
    if (prevPathname === '/create' && location.pathname !== '/create') {
      // 这里就是RecipePage被离开的时机!
      console.log('用户刚离开RecipePage');
      // 执行你的逻辑:比如保存页面状态、清理资源等
    }
    // 更新上一个路径为当前路径
    setPrevPathname(location.pathname);
  }, [location.pathname, prevPathname]);

  return null; // 这个组件不需要渲染任何内容
};
  1. 在你的根渲染代码里引入这个组件,放在Router内部、Routes外面:
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <Router>
      <RouteChangeListener />
      <Routes>
        <Route path="/" element={<App />} />
        <Route path="/settings" element={<SettingsPage />} />
        <Route path="/create" element={<RecipePage />} />
      </Routes>
    </Router>
  </React.StrictMode>
);

这个方案的好处是:全局监听所有路由变化,能明确知道用户是从哪个页面跳走的,而且不受StrictMode的重复挂载影响——因为RouteChangeListener只会在路由真的变化时触发逻辑,StrictMode的组件重复渲染不会改变路由路径。

方案二:在RecipePage内部用Ref区分StrictMode的测试卸载

如果你只想在RecipePage内部处理离开逻辑,不想搞全局监听,可以用useRef标记组件是否是正式挂载,从而过滤掉StrictMode的测试性卸载。

实现代码:

import { useEffect, useRef } from 'react';
import { useLocation } from 'react-router-dom';

const RecipePage = () => {
  const location = useLocation();
  // 标记是否是第一次挂载(StrictMode的测试挂载)
  const isFirstMount = useRef(true);
  // 标记是否是正式挂载完成
  const isOfficiallyMounted = useRef(false);

  useEffect(() => {
    if (isFirstMount.current) {
      // 第一次挂载:StrictMode的测试,直接标记后返回
      isFirstMount.current = false;
      return () => {
        // 第一次卸载:StrictMode的测试,忽略
      };
    }

    // 第二次挂载:正式进入页面
    isOfficiallyMounted.current = true;

    return () => {
      // 只有正式挂载后的卸载,才是真的离开页面
      if (isOfficiallyMounted.current) {
        console.log('RecipePage 真的被离开了,当前要跳去的路径:', location.pathname);
        // 执行你的离开逻辑:保存表单、清理定时器等
      }
    };
  }, []);

  // 页面其他代码...
  return <div>Recipe Page Content</div>;
};

原理说明:

StrictMode在开发环境下会执行mount → unmount → remount的流程,第一次的mount和unmount都是测试用的,第二次remount才是真正的页面挂载。我们用isFirstMount来跳过第一次的测试流程,只有第二次挂载后的cleanup,才判定为用户真的离开页面。

为什么之前的方法没用?

  • onBlur:它是DOM元素的焦点事件,路由切换是组件卸载,和元素焦点无关,所以完全不会触发。
  • 直接监听location.pathname的useEffect:StrictMode的重复卸载会触发cleanup,而且此时location.pathname还是/create(因为路由还没切换),所以你没法区分是测试卸载还是真的离开。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 14:07:59