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

React中如何使用react-router的usePrompt实现路由切换提示?

React Router 路由切换提示功能失效问题解决

我正在基于react-router开发提示功能,希望在切换路由(页面)时触发提示弹窗。目前已配置相关属性但功能未生效,请问有什么解决建议?如何实现所有路由切换时均显示提示信息?

你的代码

import {
  BrowserRouter as Router,
  Routes,
  Route,
  useHistory,
  usePrompt,
  useBlocker,
  Navigate
} from "react-router-dom";
import Demo from "./Demo";
import About from "./About";
import React, { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
  const [login, setLogin] = React.useState(false);

  return (
    <div className="App">
      <Demo />
      <button onClick={() => setLogin(!login)}>
        {login ? "LogOut" : "LogIn"}
      </button>
      <Routes>
        <Route exact path="/" element={<Home />} onLeave={ BlockingForm } />
        {/*Pass Props to a Component Rendered by React Router */}
        <Route path="/about" element={<About login={login} />} onLeave={ BlockingForm }/>
        {/* if you want to navigate home page if no page found  */}
        <Route
          path="*"
          element={true ? <NotFound /> : <Navigate replace="/" />}
        />
      </Routes>
    </div>
  );
}
function Home() {
  return <div>Home component</div>;
}

function NotFound() {
  return <div>404 Not Found Page</div>;
}
function BlockingForm() {
  let [isBlocking, setIsBlocking] = useState(false);

  usePrompt(
    "Hello from usePrompt -- Are you sure you want to leave?",
    isBlocking
  );
  // useBlocker(
  //   () => "Hello from useBlocker -- are you sure you want to leave?",
  //   isBlocking
  // );

  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        event.target.reset();
        setIsBlocking(false);
      }}
    >
      <p>
        Blocking? {isBlocking ? "Yes, click a link or the back button" : "Nope"}
      </p>

      <p>
        <input
          size="50"
          placeholder="type something to block transitions"
          onChange={(event) => {
            setIsBlocking(event.target.value.length > 0);
          }}
        />
      </p>

      <p>
        <button>Submit to stop blocking</button>
      </p>
    </form>
  );
}

核心问题分析

  1. React Router v6 已移除 onLeave 属性:你代码里在<Route>上使用的onLeave是v5的API,v6完全删除了这个属性,所以配置的onLeave={BlockingForm}根本不会生效。
  2. usePrompt/useBlocker 使用位置错误:这两个Hook必须在被<Router>包裹的组件内部使用,且需要直接作用于路由切换的拦截逻辑,而不是作为onLeave的参数传入。
  3. 原代码缺少<Router>包裹:整个路由系统必须被<Router>组件包裹,否则usePrompt这类依赖路由上下文的Hook无法工作。
  4. 单路由配置无法实现全局拦截:你当前的写法是给单个路由配置拦截,要实现所有路由切换都提示,需要全局层面的拦截逻辑。

解决方法

方法1:实现全局路由提示(所有切换都触发)

创建一个全局拦截组件,放在<Routes>外层,确保所有路由切换都会触发提示:

import {
  BrowserRouter as Router,
  Routes,
  Route,
  usePrompt,
  Navigate
} from "react-router-dom";
import Demo from "./Demo";
import About from "./About";
import React, { useState } from "react";
import "./styles.css";

// 全局路由拦截组件
function GlobalRouteGuard() {
  // 设为true则所有路由切换都触发提示,可通过状态动态控制拦截条件
  const [isBlocking, setIsBlocking] = useState(true);

  usePrompt("确定要离开当前页面吗?", isBlocking);

  return null; // 该组件无需渲染可视内容
}

export default function App() {
  const [login, setLogin] = React.useState(false);

  return (
    <Router> {/* 必须用Router包裹整个路由系统 */}
      <div className="App">
        <Demo />
        <button onClick={() => setLogin(!login)}>
          {login ? "LogOut" : "LogIn"}
        </button>
        <GlobalRouteGuard /> {/* 全局拦截组件放在Routes外层 */}
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About login={login} />} />
          <Route path="*" element={<NotFound />} />
        </Routes>
      </div>
    </Router>
  );
}

function Home() {
  return <div>Home component</div>;
}

function NotFound() {
  return <div>404 Not Found Page</div>;
}

方法2:针对特定页面实现拦截(比如表单页面)

如果只需要在特定页面(如未保存的表单页面)触发提示,直接在该页面组件内部使用usePrompt:

function BlockingForm() {
  const [isBlocking, setIsBlocking] = useState(false);

  // 输入框有内容时拦截路由切换
  usePrompt("确定要离开吗?输入的内容会丢失", isBlocking);

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        setIsBlocking(false);
        alert("表单提交成功");
      }}
    >
      <p>输入内容后切换路由会触发提示</p>
      <input
        placeholder="输入内容..."
        onChange={(e) => setIsBlocking(e.target.value.length > 0)}
      />
      <button type="submit">提交</button>
    </form>
  );
}

// 在路由中挂载该组件
<Route path="/form" element={<BlockingForm />} />

关键注意事项

  • 务必用<Router>包裹:所有使用路由Hook的组件都必须在<Router>的上下文范围内,否则Hook会失效。
  • 动态控制拦截条件:通过isBlocking状态变量,可以灵活控制什么时候触发提示,比如仅当表单有未保存内容时拦截。
  • 区分usePrompt和useBlocker:usePrompt是简单的确认弹窗,useBlocker支持自定义拦截逻辑(比如异步请求确认),复杂场景可选用useBlocker。

内容的提问来源于stack exchange,提问作者Sougata Mukherjee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 04:45:13