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

react-router-dom ^6.3.0中函数组件如何使用<Prompt />做路由拦截?

React Router v6.3.0 实现离开页面确认提示方案

React Router v6 版本移除了v5中内置的<Prompt />组件,要覆盖误触刷新、返回按钮、手动改URL、站内跳转等所有离开场景,需要分别处理浏览器原生离开行为和React Router管控的站内路由跳转两类场景。


1. 处理浏览器原生场景(刷新、关闭标签页、手动输入外站URL跳转)

这类场景由浏览器原生管控,通过监听beforeunload事件实现拦截,封装为自定义Hook即可复用:

import { useEffect } from 'react';

// 传入when参数控制是否开启拦截,message为提示文案
const useBeforeUnloadPrompt = (when = true, message = '当前页面有未保存的内容,确定要离开吗?') => {
  useEffect(() => {
    if (!when) return;

    const handleBeforeUnload = (e) => {
      e.preventDefault();
      // 兼容旧版浏览器,现代浏览器会忽略自定义message,展示系统默认提示
      e.returnValue = message;
      return message;
    };

    window.addEventListener('beforeunload', handleBeforeUnload);
    return () => window.removeEventListener('beforeunload', handleBeforeUnload);
  }, [when, message]);
};

注意:出于安全规范,Chrome、Edge、Firefox等现代浏览器不会展示自定义的提示文案,会固定展示浏览器默认的离开确认提示,该限制无法通过前端代码绕过。


2. 处理React Router站内跳转场景(返回按钮、站内链接点击、编程式导航)

这类跳转由React Router管控,v6.3.0版本已经内置了拦截能力,只是标记为实验性API,逻辑稳定可直接生产使用:

// 直接从react-router-dom导入,重命名为usePrompt方便后续版本升级替换
import { unstable_usePrompt as useRouterPrompt } from 'react-router-dom';

如果不想使用带unstable_前缀的API,也可以基于路由上下文自己实现拦截Hook:

import { useContext, useEffect, useRef } from 'react';
import { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom';

const useRouterPrompt = (when = true, message = '当前页面有未保存的内容,确定要离开吗?') => {
  const { navigator } = useContext(NavigationContext);
  const unblockRef = useRef(null);

  useEffect(() => {
    if (!when) {
      unblockRef.current?.();
      unblockRef.current = null;
      return;
    }

    // 调用history实例的block方法拦截路由跳转
    unblockRef.current = navigator.block((transition) => {
      const isAllowLeave = window.confirm(message);
      if (isAllowLeave) {
        // 解除拦截后重试跳转
        unblockRef.current?.();
        transition.retry();
      }
    });

    return () => {
      unblockRef.current?.();
      unblockRef.current = null;
    };
  }, [when, message, navigator]);
};

如果需要使用自定义React弹窗组件替代原生window.confirm,只需要把上面window.confirm的逻辑替换为你自己的弹窗状态控制,用户点击确认按钮后再执行unblockRef.current?.()和transition.retry()即可支持异步确认。


3. 实际使用示例

在需要拦截的函数组件中,同时调用两个Hook覆盖所有场景,通过业务状态控制拦截是否生效:

const FormEditPage = () => {
  // 业务状态:比如表单是否被修改过,初始为false
  const [hasUnsavedChange, setHasUnsavedChange] = useState(false);

  // 覆盖刷新/关页场景
  useBeforeUnloadPrompt(hasUnsavedChange);
  // 覆盖站内路由跳转场景
  useRouterPrompt(hasUnsavedChange);

  // 表单提交成功、重置表单时,记得把hasUnsavedChange设为false,避免正常跳转被拦截
  const handleSubmit = async () => {
    await submitForm();
    setHasUnsavedChange(false);
    navigate('/list');
  };

  return <div>/* 表单内容 */</div>
};

注意事项

  • 两类拦截必须同时配置,缺任何一个都会存在漏拦截的场景
  • 拦截生效的判断条件必须和业务状态严格同步,避免正常操作被拦截
  • 不要在beforeunload的监听逻辑里加异步逻辑,该事件不支持等待异步执行结果

内容的提问来源于stack exchange,提问作者Vishnu Kumar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:27:03