在Electron-Vite+React+TypeScript项目中,如何在StrictMode下可靠监听RecipePage路由页面的离开事件?
在Electron-Vite+React+TypeScript项目中,如何在StrictMode下可靠监听RecipePage路由页面的离开事件?
太懂你被StrictMode坑到的烦躁了!开发环境下它会故意重复挂载/卸载组件来检测副作用,直接用useEffect监听location.pathname根本分不清是真的离开页面,还是StrictMode在搞测试,而且onBlur完全不适用——因为路由切换是组件卸载,不是元素失去焦点,自然没效果。
下面给你两个靠谱的解决方案,按需选择就行:
方案一:全局路由监听+追踪上一个路径
这个思路是在路由根节点加一个全局监听组件,对比当前路由和上一个路由,精准判断是不是从/create页面离开的,完美避开StrictMode的干扰。
实现步骤:
- 先写一个全局路由监听组件,用
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; // 这个组件不需要渲染任何内容 };
- 在你的根渲染代码里引入这个组件,放在
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
相关产品推荐
相关产品推荐

