React 18.2函数组件内导航时如何阻止默认行为并触发未保存提示?
解决方法
你的问题出在:beforeunload 事件只负责处理页面刷新、关闭或者跳转到外部链接的情况,而 React Router 的内部路由跳转是客户端行为,不会触发这个事件,所以弹窗没反应。
要覆盖所有场景,得同时处理浏览器卸载和 React Router 的导航拦截。下面是完整代码:
import { useLocation, useParams, useNavigate, useBlocker } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { useEffect } from 'react'; const YourComponent = () => { const dispatch = useDispatch(); const location = useLocation(); const navigate = useNavigate(); const { id } = useParams(); // 拦截应用内路由跳转 useBlocker((tx) => { if (!id) { if (window.confirm('您所做的更改可能未保存,确定要离开吗?')) { tx.proceed(); } else { tx.abort(); } } else { tx.proceed(); } }); // 处理页面刷新、外部跳转 useEffect(() => { const handleBeforeUnload = (event) => { if (!id) { event.preventDefault(); event.returnValue = ''; return ''; } }; window.addEventListener('beforeunload', handleBeforeUnload); return () => window.removeEventListener('beforeunload', handleBeforeUnload); }, [id]); // 依赖id,确保条件变化时更新监听 return ( // 你的组件内容 ); }; export default YourComponent;
几个关键点:
useBlocker:React Router v6.4+ 自带的钩子,专门拦截客户端路由跳转。通过tx.proceed()允许跳转,tx.abort()取消跳转。- 自定义提示:浏览器对
beforeunload的文案有限制,没法直接设置自定义内容,但内部路由跳转可以用window.confirm显示你想要的提示。 - 依赖更新:把
id加到useEffect的依赖里,这样当id变化时,监听逻辑会自动更新。
如果你的 React Router 版本低于 v6.4,用下面的方式替代 useBlocker:
useEffect(() => { const unblock = navigate((tx) => { if (!id) { if (window.confirm('您所做的更改可能未保存,确定要离开吗?')) { tx.unblock(); tx.retry(); } } }); return unblock; }, [id, navigate]);
这样不管是刷新页面、跳外部链接还是内部路由跳转,只要未保存更改(这里用 !id 判断),都会弹出提示。
内容的提问来源于stack exchange,提问作者Muhammad Usman
相关产品推荐
相关产品推荐

