ByteNoteByteNote

字节笔记本

2026年5月3日

TanStack Start - Error Boundaries 错误边界

API中转
¥120

TanStack Start 基于 TanStack Router 的路由级错误边界系统,提供灵活的错误处理机制。

概述

  • 全局默认错误处理: 通过路由器为所有路由设置默认错误组件
  • 路由级覆盖: 每个路由可以自定义错误处理
  • 错误捕获: 在 beforeLoadloader 中抛出的错误会被自动捕获
  • 重试机制: 通过 reset() 函数重新渲染路由

全局默认错误组件

src/router.tsx 中为所有路由设置默认错误组件:

tsx
import { createRouter, ErrorComponent } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

export function getRouter() {
  const router = createRouter({
    routeTree,
    defaultErrorComponent: ({ error, reset }) => (
      <ErrorComponent error={error} />
    ),
  })
  return router
}

路由级错误组件

每个路由可以定义自己的错误组件来覆盖全局默认值:

tsx
import { createFileRoute, ErrorComponent } from '@tanstack/react-router'

function PostError({ error, reset }: ErrorComponentProps) {
  return (
    <div className="p-4 bg-red-50 rounded-lg">
      <h2 className="text-xl font-bold text-red-800">Failed to Load</h2>
      <p className="text-red-600 mt-2">{error.message}</p>
      <button onClick={reset} className="mt-4 px-4 py-2 bg-red-600 text-white rounded">
        Retry
      </button>
    </div>
  )
}

export const Route = createFileRoute('/posts/$postId')({
  component: PostComponent,
  errorComponent: PostError,
})

错误捕获机制

在 Loader 中抛出错误

tsx
export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => {
    const post = await fetchPost(params.postId)
    if (!post) {
      throw new Error('Post not found!')
    }
    return post
  },
  errorComponent: PostError,
})

在 beforeLoad 中抛出错误

tsx
export const Route = createFileRoute('/admin')({
  beforeLoad: async ({ context }) => {
    const user = await getCurrentUser()
    if (!user.isAdmin) {
      throw new Error('Access denied: Admin only')
    }
  },
  errorComponent: AdminError,
})

重试机制

reset() 函数允许用户在修复状态后重新渲染路由:

tsx
function PostError({ error, reset }: ErrorComponentProps) {
  const handleRetry = () => {
    clearCache()
    reset()
  }

  return (
    <div>
      <h2>Error loading post</h2>
      <p>{error.message}</p>
      <button onClick={handleRetry}>Retry</button>
    </div>
  )
}

嵌套路由错误处理

在嵌套路由中,错误会冒泡到最近的错误边界:

tsx
// __root.tsx
export const Route = createRootRoute({
  component: RootLayout,
  errorComponent: RootError, // 捕获所有未处理的错误
})

// dashboard.tsx
export const Route = createFileRoute('/dashboard')({
  component: Dashboard,
  errorComponent: DashboardError, // 捕获 dashboard 相关错误
})

// dashboard.settings.tsx
export const Route = createFileRoute('/dashboard/settings')({
  component: Settings,
  // 没有错误组件,错误冒泡到 dashboard 或 root
})

错误类型处理

Not Found 错误

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

export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => {
    const post = await fetchPost(params.postId)
    if (!post) {
      throw notFound()
    }
    return post
  },
})

重定向错误

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

export const Route = createFileRoute('/protected')({
  beforeLoad: async () => {
    const isAuthenticated = await checkAuth()
    if (!isAuthenticated) {
      throw redirect({ to: '/login' })
    }
  },
})

最佳实践

  1. 提供有用的错误信息 - 不要只显示 error.message
  2. 提供重试选项 - 使用 reset() 函数
  3. 开发环境显示堆栈 - 仅在 DEV 模式下显示
  4. 记录错误 - 发送到错误跟踪服务

API 速查

API位置用途
defaultErrorComponentrouter.tsx全局默认错误组件
errorComponent路由配置路由级错误组件
reset()错误组件重试渲染路由
ErrorComponent内置组件简单的错误 UI

错误处理流程

text
用户操作
    │
    ▼
beforeLoad / loader
    │
    ├─ 成功 → 渲染组件
    │
    └─ 失败 → 抛出错误
              │
              ▼
        路由 errorComponent (如果存在)
              │
              └─ 不存在 → 冒泡到父路由 errorComponent
                        │
                        └─ 最终到达 defaultErrorComponent
分享: