You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 14:37:22