如何在react-router-dom V6中实现页面离开前确认提示?
React Router Dom v6 页面离开确认弹窗修复方案
问题描述
需要实现用户离开页面时弹出确认提示的功能,基于React Router Dom v5的示例适配v6时,navigate.block()方法无法正常工作,导致确认逻辑失效,无法在用户确认后完成页面跳转。
当前适配后的代码:
import React, { useState, useCallback, useEffect } from "react"; import { Link, useNavigate } from "react-router-dom"; import ExitDialog from "./ExitDialog"; const TestingPage = () => { const navigate = useNavigate(); const [isVisibleDialog, setVisibleDialog] = useState(false); const [triggerExit, setTriggerExit] = useState({ onOk: false, path: "" }); const handleGoToIntendedPage = useCallback( (location) => navigate(location), [navigate] ); useEffect(() => { if (triggerExit.onOk) { handleGoToIntendedPage(triggerExit.path); } const unblock = navigate.block((location) => { if (location.pathname !== "/testing") { setVisibleDialog(true); } setTriggerExit((obj) => ({ ...obj, path: location.pathname })); if (triggerExit.onOk) { return true; } return false; }); return () => { unblock(); }; }, [handleGoToIntendedPage, navigate, triggerExit.onOk, triggerExit.path]); return ( <div> <div> <h1>You are currently in Testing Page</h1> </div> <div> <Link to="/">Home</Link> </div> <ExitDialog visible={isVisibleDialog} onClose={() => setVisibleDialog(false)} onOk={() => { setTriggerExit((obj) => ({ ...obj, onOk: true })); setVisibleDialog(false); }} /> </div> ); }; export default TestingPage;
问题根源
React Router Dom v6的navigate.block()和v5的history.block()逻辑存在核心差异:
- v5中
block()可返回提示文本触发原生确认框,或返回false阻止导航; - v6中
navigate.block()的回调需直接返回**目标导航位置(允许跳转)**或false(阻止跳转),且回调同步执行,无法依赖异步更新的组件状态(比如triggerExit.onOk)。
同时原代码中useEffect依赖状态频繁重创建阻塞函数,导致逻辑混乱。
修复方案
调整逻辑,分离"阻止导航"和"确认后跳转"的流程,用独立状态存储待跳转位置,简化阻塞函数的使用:
import React, { useState, useEffect } from "react"; import { Link, useNavigate } from "react-router-dom"; import ExitDialog from "./ExitDialog"; const TestingPage = () => { const navigate = useNavigate(); const [isVisibleDialog, setVisibleDialog] = useState(false); const [pendingLocation, setPendingLocation] = useState(null); // 存储用户想要跳转的目标位置 useEffect(() => { const unblock = navigate.block((location) => { // 若目标是当前页面,允许导航(比如页面刷新场景) if (location.pathname === "/testing") { return true; } // 否则阻止导航,弹出确认弹窗 setPendingLocation(location); setVisibleDialog(true); return false; }); // 组件卸载时解除阻塞 return () => unblock(); }, [navigate]); // 用户确认离开的处理逻辑 const handleConfirmExit = () => { setVisibleDialog(false); if (pendingLocation) { // 先解除所有导航阻塞,再执行跳转 navigate.block(() => true); navigate(pendingLocation); } }; // 用户取消离开的处理逻辑 const handleCancelExit = () => { setVisibleDialog(false); setPendingLocation(null); }; return ( <div> <div> <h1>You are currently in Testing Page</h1> </div> <div> <Link to="/">Home</Link> </div> <ExitDialog visible={isVisibleDialog} onClose={handleCancelExit} onOk={handleConfirmExit} /> </div> ); }; export default TestingPage;
关键改进点
- 状态拆分:用
pendingLocation单独存储待跳转位置,避免复合状态导致的逻辑混乱; - 正确使用阻塞函数:在
navigate.block()回调中直接返回false阻止当前跳转,仅在用户确认后手动解除阻塞并执行导航; - 简化依赖:
useEffect仅依赖navigate,避免状态变化重复创建阻塞函数,减少不必要的重渲染; - 明确跳转逻辑:确认离开时先解除阻塞,再执行跳转,确保流程清晰可控。
内容的提问来源于stack exchange,提问作者Katyellen
相关产品推荐
相关产品推荐

