如何阻止浏览器返回跳转,直至用户确认window.confirm弹窗?
解决浏览器返回按钮确认后取消仍回退的问题
核心问题分析
- 原代码使用
history.listen监听路由变化,该回调在路由跳转之后触发,无法阻止默认的回退行为,所以用户取消确认后页面已经完成跳转。 isOnce变量定义在useEffect内部,每次usernameToSend变化都会重新初始化,无法跨渲染周期保持状态,导致重复弹窗问题。
修复方案
使用window.popstate事件(可阻止默认行为)+ useRef保存状态(跨渲染周期),同时手动控制历史栈状态:
import { useEffect, useRef } from 'react'; // 假设exitToServer已改为返回Promise的异步函数 // 如果未改,需要封装为Promise形式: // const exitToServer = () => { // return new Promise((resolve) => { // // 原有HTTP请求逻辑,完成后调用resolve() // fetch('/api/exit', { method: 'POST', body: JSON.stringify({ username: usernameToSend }) }) // .then(res => res.json()) // .then(() => resolve()) // .catch(() => resolve()); // }); // }; useEffect(() => { const isProcessing = useRef(false); // 用useRef跨渲染保存状态 const handlePopState = (e: PopStateEvent) => { if (!usernameToSend || isProcessing.current) return; e.preventDefault(); // 先阻止默认回退行为 const shouldLeave = window.confirm("Leaving so soon? Chat data will be lost."); if (shouldLeave) { isProcessing.current = true; // 执行退出请求,完成后再触发回退 exitToServer().finally(() => { window.history.goBack(); // 请求完成后移除监听,避免后续触发 window.removeEventListener('popstate', handlePopState); }); } else { // 用户取消,手动将当前路径重新推入历史栈,恢复栈状态 window.history.pushState(null, '', window.location.pathname); isProcessing.current = false; } }; window.addEventListener('popstate', handlePopState); // 组件卸载或依赖变化时移除事件监听,防止内存泄漏 return () => { window.removeEventListener('popstate', handlePopState); }; }, [usernameToSend]);
关键修改点说明
- 改用
popstate事件:该事件在浏览器回退行为触发前执行,可通过e.preventDefault()阻止默认跳转。 useRef保存状态:isProcessing的current属性会在组件整个生命周期内保持值,避免每次渲染重新初始化导致的重复弹窗。- 手动维护历史栈:用户取消时,调用
history.pushState将当前路径重新加入历史栈,抵消popstate触发的栈弹出操作,确保页面停留在当前页。 - 异步请求处理:等待
exitToServer请求完成后再执行回退,避免请求未完成就跳转页面,同时请求完成后移除监听防止重复触发。
内容的提问来源于stack exchange,提问作者MiniM
相关产品推荐
相关产品推荐

