React页面跳转时无法检测页面离开事件的问题求助
React未保存修改警告:覆盖浏览器卸载与路由跳转场景
你的现有代码仅监听了beforeunload事件,这个事件只在浏览器刷新、关闭标签页这类页面卸载场景触发。React单页应用的路由跳转属于内部页面切换,不会触发浏览器卸载,所以需要额外添加路由导航拦截逻辑。
完整解决方案
1. 保留浏览器卸载监听(处理刷新/关闭标签页)
这部分你的原有代码可以直接保留,负责拦截浏览器层面的页面卸载操作:
useEffect(() => { const handleBeforeUnload = (e) => { if (hasUnsavedChanges) { e.preventDefault(); e.returnValue = ''; return e.returnValue; } }; window.addEventListener('beforeunload', handleBeforeUnload); return () => window.removeEventListener('beforeunload', handleBeforeUnload); }, [hasUnsavedChanges]);
2. 添加React路由跳转拦截(处理内部页面跳转)
根据你使用的React Router版本,选择对应的实现方式:
React Router v6(当前主流版本)
使用useNavigate的block方法拦截导航:
import { useNavigate } from 'react-router-dom'; // 在组件内部 const navigate = useNavigate(); useEffect(() => { const unblock = navigate((location) => { if (hasUnsavedChanges) { const shouldLeave = window.confirm('你有未保存的修改,确定要离开吗?'); if (shouldLeave) { unblock(); // 解除拦截,允许跳转 navigate(location); } return false; // 阻止跳转 } return true; // 允许正常跳转 }); // 组件卸载时解除拦截 return () => unblock(); }, [hasUnsavedChanges, navigate]);
React Router v5及以下版本
使用useHistory的block方法:
import { useHistory } from 'react-router-dom'; // 在组件内部 const history = useHistory(); useEffect(() => { const unblock = history.block((location) => { if (hasUnsavedChanges) { return '你有未保存的修改,确定要离开吗?'; } }); return () => unblock(); }, [hasUnsavedChanges, history]);
整合后的完整组件示例
import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; const EditableForm = ({ hasUnsavedChanges }) => { const navigate = useNavigate(); // 拦截浏览器卸载(刷新/关闭标签页) useEffect(() => { const handleBeforeUnload = (e) => { if (hasUnsavedChanges) { e.preventDefault(); e.returnValue = ''; return e.returnValue; } }; window.addEventListener('beforeunload', handleBeforeUnload); return () => window.removeEventListener('beforeunload', handleBeforeUnload); }, [hasUnsavedChanges]); // 拦截React路由跳转 useEffect(() => { const unblock = navigate((location) => { if (hasUnsavedChanges) { const shouldLeave = window.confirm('你有未保存的修改,确定要离开当前页面吗?'); if (shouldLeave) { unblock(); navigate(location); } return false; } return true; }); return () => unblock(); }, [hasUnsavedChanges, navigate]); return ( <div> {/* 你的表单或可编辑内容 */} </div> ); }; export default EditableForm;
注意事项
- 浏览器对
beforeunload的提示文案有统一限制,无法完全自定义;window.confirm的文案可以自定义,但样式由浏览器控制。 - 组件卸载时一定要解除路由拦截(调用
unblock()),避免内存泄漏或影响其他页面逻辑。
内容的提问来源于stack exchange,提问作者Abdul Rehman
相关产品推荐
相关产品推荐

