React Router V6下阻止未保存修改时应用内页面导航的方案
React Router v6 应用内未保存修改导航拦截方案
针对React Router v6移除Prompt/usePrompt后的导航拦截需求,以下是两种可行方案,结合你的isDirty状态实现完整的拦截逻辑:
方案一:使用useNavigation的block方法(推荐)
该方法能拦截所有应用内导航动作(包括Link点击、浏览器前进后退、navigate调用),同时搭配useBeforeUnload处理页面刷新/外部跳转:
import { useNavigation, useBeforeUnload, useCallback } from 'react-router-dom'; import { useEffect, useState } from 'react'; function YourEditComponent() { const [isDirty, setIsDirty] = useState(false); const navigation = useNavigation(); // 拦截应用内导航 useEffect(() => { const unblock = navigation.block((tx) => { if (!isDirty) { tx.retry(); return; } const confirmLeave = window.confirm('你有未保存的修改,确定要离开吗?'); if (confirmLeave) { tx.retry(); } else { tx.abort(); } }); return unblock; }, [navigation, isDirty]); // 处理页面刷新/外部跳转 useBeforeUnload( useCallback((e) => { if (isDirty) { e.preventDefault(); (e as BeforeUnloadEvent).returnValue = ''; } }, [isDirty]) ); // 其他组件逻辑... return ( // 你的表单内容... ); }
关键说明:
navigation.block接收的回调中,tx是导航事务对象:tx.retry():允许继续执行导航tx.abort():终止当前导航
- 组件卸载时必须调用
unblock清理拦截,避免内存泄漏
方案二:自定义拦截式链接组件(补充方案)
如果只需要拦截通过Link触发的导航,可以自定义一个ConfirmLink组件替代原生Link:
import { Link, useNavigate } from 'react-router-dom'; import type { LinkProps } from 'react-router-dom'; type ConfirmLinkProps = LinkProps & { isDirty: boolean; confirmMessage?: string; }; function ConfirmLink({ isDirty, confirmMessage = '你有未保存的修改,确定要离开吗?', ...props }: ConfirmLinkProps) { const navigate = useNavigate(); const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => { if (isDirty) { e.preventDefault(); const confirmLeave = window.confirm(confirmMessage); if (confirmLeave) { navigate(props.to as string); } } }; return <Link {...props} onClick={handleClick} />; } // 使用方式 <ConfirmLink to="/other-page" isDirty={isDirty}>跳转到其他页面</ConfirmLink>
注意:
- 该方案仅能拦截
ConfirmLink的点击事件,无法处理浏览器前进后退、直接调用navigate的情况,需搭配方案一使用才能覆盖所有场景。
内容的提问来源于stack exchange,提问作者Ira
相关产品推荐
相关产品推荐

