---
id: router.navigation-blocker
kind: recipe
product: router
framework: react
locale: zh-CN
revision: 2
sourceCheckedOn: "2026-08-03"
versionRange: "^1"
verifiedAgainst: "@tanstack/react-router@1.170.18"
contentModel: 2
packages:
  - "@tanstack/react-router"
tasks:
  - "block-navigation"
  - "protect-dirty-form"
  - "confirm-route-leave"
sourceRefs:
  - "https://tanstack.com/router/latest/docs/guide/navigation-blocking"
---

# 保护脏表单的离页导航

> 用 useBlocker 的 resolver 状态实现可访问的离页确认，并按脏状态注册 beforeunload。

## 执行契约

- **适用场景:** 离开当前路由会丢失明确可检测的未保存用户输入。
- **不要使用:** 不要阻止只会丢失可重新获取数据或无价值临时 UI 状态的导航。
- **前置检查:** 定义可靠的 isDirty、保存成功后的重置动作，以及浏览器卸载策略。
- **验证结果:** 覆盖 Link、前进后退、刷新、关闭标签页和保存后离开；未修改状态不得出现提示。
- **失败模式:** 保存后仍提示通常表示 Mutation 成功路径没有同步重置 dirty 状态。
- **安全边界:** 阻止导航只能保护体验，不能作为持久化、草稿恢复或并发写入控制。

## 阻塞语义

shouldBlockFn 返回 true 表示阻止。withResolver: true 时，其返回值只决定是否进入 blocked 状态；之后必须由 proceed 或 reset 解决本次导航。

## 最小 resolver

enableBeforeUnload 使用相同脏状态，覆盖刷新与关闭标签页。自定义对话框应有明确可访问名称、键盘操作和焦点管理；下面只展示 Router 状态契约。

File: `src/features/editor/useLeaveBlocker.ts`

```tsx
import { useBlocker } from '@tanstack/react-router'

export function useLeaveBlocker(isDirty: boolean) {
  return useBlocker({
    shouldBlockFn: () => isDirty,
    enableBeforeUnload: isDirty,
    withResolver: true,
  })
}
```

## 验证清单

验证干净表单不阻塞；脏表单的 Link、后退、刷新和关闭标签页得到正确行为；proceed 继续，reset 留下；保存成功清除脏状态。浏览器对 beforeunload 使用系统对话框，文案不可完全自定义。

## Official sources

- https://tanstack.com/router/latest/docs/guide/navigation-blocking
