React 第四十三节 Router中 useBlocker 的使用详解及案例注意事项

前言

useBlockerReact Router 中用于阻止用户导航的钩子 ,它允许开发者在特定条件下(例如表单未保存时)拦截用户的导航行为(如点击链接、回退按钮等),并触发自定义逻辑(如显示确认对话框)。与 unstable_usePrompt 类似,但 useBlocker 提供更底层的控制能力。

注意:useBlocker 是实验性 API

一、useBlocker 核心用途

1.1、拦截导航行为:

复制代码
阻止用户通过路由跳转、浏览器后退/前进按钮离开当前页面。

1.2、自定义确认逻辑:

复制代码
灵活控制是否允许导航,并可结合弹窗组件实现更友好的交互。

1.3、异步处理:

复制代码
支持在用户确认后执行异步操作(如保存数据)再允许导航。

二、useBlocker 基本使用

javascript 复制代码
import { useBlocker } from "react-router-dom";

function EditForm() {
  const [isDirty, setIsDirty] = useState(false);

  // 使用 useBlocker 拦截导航
  const blocker = useBlocker(
    ({ currentLocation, nextLocation }) => 
      isDirty && currentLocation.pathname !== nextLocation.pathname
  );

  // 当拦截被触发时,显示确认对话框
  useEffect(() => {
    if (blocker.state === "blocked") {
      const confirm = window.confirm(
        "您有未保存的更改,确定要离开吗?"
      );

      if (confirm) {
        blocker.proceed(); // 用户确认离开,放行导航
      } else {
        blocker.reset();   // 用户取消,重置拦截状态
      }
    }
  }, [blocker.state]);

  return (
    <form>
      <input 
        type="text" 
        onChange={(e) => setIsDirty(e.target.value !== "")}
      />
    </form>
  );
}

三、useBlocker参数说明

useBlocker 接收一个函数 作为参数,该函数返回 true 时触发拦截:

javascript 复制代码
useBlocker(
  (navigation: Navigation) => boolean
);

navigation 对象:包含导航的详细信息: currentLocation:当前路由位置。 nextLocation:目标路由位置。 formData:如果是表单提交导航,包含表单数据。 formMethod:表单的 HTTP 方法(如 "post")。

3.1、返回对象

useBlocker 返回一个 Blocker 对象,包含以下属性:

state : String类型字符串 ,拦截状态, 值为:"unblocked"(未拦截)、"blocked"(已拦截)、"proceeding"(正在放行) proceed() : 回调函数() => void: 手动放行被拦截的导航 reset() : 回调函数() => void: 重置拦截状态

四、useBlocker完整案例:自定义弹窗拦截

4.1、useBlocker 定义拦截逻辑

javascript 复制代码
import { useBlocker } from "react-router-dom";
import { useState } from "react";
import ConfirmationModal from "./ConfirmationModal"; // 自定义弹窗组件

function UserProfileEditor() {
  const [isDirty, setIsDirty] = useState(false);
  const blocker = useBlocker(({ currentLocation, nextLocation }) => {
    return isDirty && currentLocation.pathname !== nextLocation.pathname;
  });

  // 处理弹窗确认/取消
  const handleConfirm = async () => {
    // 可选:在放行前保存数据
    await saveData();
    blocker.proceed();
  };

  const handleCancel = () => {
    blocker.reset();
  };

  return (
    <>
      <form>
        <input 
          type="text" 
          onChange={(e) => setIsDirty(e.target.value !== "")}
        />
      </form>

      {/* 自定义弹窗 */}
      <ConfirmationModal
        isOpen={blocker.state === "blocked"}
        message="未保存的更改将丢失,确定离开?"
        onConfirm={handleConfirm}
        onCancel={handleCancel}
      />
    </>
  );
}

4.2、 useBlocker 自定义弹窗组件 (ConfirmationModal)

javascript 复制代码
function ConfirmationModal({ isOpen, message, onConfirm, onCancel }) {
  if (!isOpen) return null;

  return (
    <div className="modal-overlay">
      <div className="modal">
        <p>{message}</p>
        <button onClick={onConfirm}>离开</button>
        <button onClick={onCancel}>取消</button>
      </div>
    </div>
  );
}

五、useBlocker 使用注意事项

5.1、兼容性

useBlockerReact Router 的实验性 API(可能在 v6.x 中标为 unstable_useBlocker),未来版本可能调整 API 或行为。

需使用 React Router v6.4+ 并启用数据路由(createBrowserRouter)。

5.2、拦截范围

只能拦截 客户端路由导航 (如 <Link>、navigate()),无法阻止页面刷新或关闭 (需配合 useBeforeUnload)。 useBeforeUnload 详细介绍分析

示例:同时处理路由跳转和页面关闭:

javascript 复制代码
// 拦截路由跳转
useBlocker(shouldBlock);

// 拦截页面关闭/刷新
useBeforeUnload(() => {
  if (shouldBlock) return "确认离开?";
});

5.3、useBlocker性能优化

避免在拦截条件函数中执行高开销操作(如复杂计算、API 请求),应快速返回布尔值

与表单的交互

使用 <Form> 提交时,默认会绕过 useBlocker。如需拦截表单提交,需手动处理:

javascript 复制代码
const submitHandler = (e) => {
  e.preventDefault();
  if (isDirty) {
    showConfirmationModal(() => {
      e.target.submit(); // 用户确认后提交
    });
  }
};

5.4、useBlocker 重置状态

导航被拦截后,必须调用 proceed() 或 reset(),否则应用会卡在阻塞状态

六 、useBlocker 与 unstable_usePrompt 的对比

总结

useBlocker 是处理复杂导航拦截需求的强大工具 ,适合需要自定义交互(如美观弹窗)或异步操作的场景。

注意 :其实验性状态浏览器限制 ,建议在关键流程(如数据丢失防护)中结合 useBeforeUnload 实现全覆盖拦截。

相关推荐
漂流瓶jz5 小时前
Webpack中各种devtool配置的含义与SourceMap生成逻辑
前端·javascript·webpack
前端架构师-老李5 小时前
React 中 useCallback 的基本使用和原理解析
前端·react.js·前端框架
木易 士心6 小时前
CSS 中 `data-status` 的使用详解
前端·css
明月与玄武6 小时前
前端缓存战争:回车与刷新按钮的终极对决!
前端·缓存·回车 vs 点击刷新
牧马少女6 小时前
css 画一个圆角渐变色边框
前端·css
zy happy6 小时前
RuoyiApp 在vuex,state存储nickname vue2
前端·javascript·小程序·uni-app·vue·ruoyi
小雨青年6 小时前
Cursor 项目实战:AI播客策划助手(二)—— 多轮交互打磨播客文案的技术实现与实践
前端·人工智能·状态模式·交互
小光学长7 小时前
基于Vue的儿童手工创意店管理系统as8celp7(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js
meichaoWen7 小时前
【Vue】Vue框架的基础知识强化
前端·javascript·vue.js
jingling5557 小时前
Flutter | 基础环境配置和创建flutter项目
前端·flutter