React实现全场景自定义页面离开确认弹窗的方法
覆盖全场景页面离开确认实现方案
方案覆盖范围
- 关闭浏览器标签页、刷新页面场景
- 浏览器前进/返回按钮触发的路由跳转场景
- React Router 内Link组件、
navigate方法触发的路由跳转场景 - 自定义点击事件触发的页面跳转场景
- 支持展示自定义提示内容:离开前请将数据保存至草稿
具体实现步骤
1. 修复原生页面卸载事件绑定逻辑
原有代码的事件绑定时机错误,在组件卸载阶段才绑定onbeforeunload,会导致事件监听不生效。正确绑定方式如下,该部分负责覆盖关闭标签页、刷新页面的场景:
useEffect(() => { const handleBeforeUnload = (e) => { // 仅当存在未保存数据时触发拦截 if (!isDirty) return; e.preventDefault(); // 现代浏览器出于安全限制,会忽略自定义返回文本,统一展示系统默认提示 e.returnValue = ''; return ''; }; window.addEventListener('beforeunload', handleBeforeUnload); return () => { window.removeEventListener('beforeunload', handleBeforeUnload); }; }, [isDirty]); // isDirty为业务中标记「存在未保存数据」的状态变量
2. 封装路由拦截钩子,覆盖React Router单页内跳转场景
新版React Router 虽然移除了官方导出的useBlocker、usePrompt,但底层仍保留了history实例的拦截能力,可基于上下文自行封装拦截钩子,覆盖浏览器前进/返回、Link跳转、代码调用navigate跳转的所有单页内路由场景:
import { useContext, useEffect, useRef } from 'react'; import { UNSAFE_NavigationContext } from 'react-router-dom'; // 自定义路由拦截钩子 const useRouteBlocker = (when, onBlock) => { const { navigator } = useContext(UNSAFE_NavigationContext); const blockCallbackRef = useRef(onBlock); useEffect(() => { blockCallbackRef.current = onBlock; }, [onBlock]); useEffect(() => { if (!when) return; // 注册路由拦截 const unregisterBlock = navigator.block((navigationAction) => { const resumeNavigation = () => { unregisterBlock(); navigationAction.retry(); }; blockCallbackRef.current(resumeNavigation); }); return unregisterBlock; }, [when, navigator]); };
3. 结合自定义弹窗实现自定义提示
调用上述封装的钩子,拦截后唤起业务中的自定义弹窗,完全自定义提示内容、样式、交互:
const [showLeaveConfirm, setShowLeaveConfirm] = useState(false); const [resumeAction, setResumeAction] = useState(null); // 拦截路由跳转 useRouteBlocker(isDirty, (resume) => { setResumeAction(() => resume); setShowLeaveConfirm(true); }); // 弹窗确认离开逻辑 const handleConfirmLeave = () => { setShowLeaveConfirm(false); resumeAction?.(); }; // 弹窗取消离开逻辑 const handleCancelLeave = () => { setShowLeaveConfirm(false); setResumeAction(null); };
在自定义弹窗组件中传入open={showLeaveConfirm},确认按钮绑定handleConfirmLeave,取消按钮绑定handleCancelLeave,弹窗内容直接写离开前请将数据保存至草稿即可。
4. 覆盖自定义点击事件触发的跳转
如果是业务代码中手动绑定点击事件触发的跳转,在跳转逻辑前增加拦截判断即可,复用同一套弹窗逻辑:
const handleCustomJump = (targetPath) => { if (isDirty) { setShowLeaveConfirm(true); setResumeAction(() => () => navigate(targetPath)); return; } navigate(targetPath); };
注意:受浏览器安全策略限制,关闭标签页、刷新页面触发的系统级弹窗无法自定义文案,所有单页应用内的跳转场景都可以完全使用自定义弹窗展示指定提示内容。
内容的提问来源于stack exchange,提问作者Maher Naveed
相关产品推荐
相关产品推荐

