⚠️ 此页面涵盖了更新的 notFound 函数和 notFoundComponent API,用于处理未找到错误。NotFoundRoute 路由已被弃用,并将在未来版本中移除。请参阅 从 NotFoundRoute 迁移 以获取更多信息。
在 TanStack Router 中,未找到错误有两种用途
在底层,这两种情况都是使用相同的 notFound 函数和 notFoundComponent API 实现的。
notFoundMode
选项当 TanStack Router 遇到与任何已知路由模式不匹配的 pathname 或 部分匹配路由模式但带有额外尾随 pathname 段时,它将自动抛出未找到错误。
根据 notFoundMode 选项,路由器将以不同的方式处理这些自动错误:
默认情况下,路由器的 notFoundMode 设置为 fuzzy,这表示如果 pathname 与任何已知路由不匹配,路由器将尝试使用具有子路由(以及插座)和配置了未找到组件的最接近匹配路由。
❓ 为什么这是默认设置? 模糊匹配以尽可能多地为用户保留父布局,这为他们提供了更多上下文,以便根据他们认为将到达的位置导航到有用的位置。
最近的合适路由使用以下标准找到
例如,考虑以下路由树
如果提供了 /posts/1/edit 的路径,将渲染以下组件结构
posts 路由的 notFoundComponent 将被渲染,因为它是最近的合适的父路由,具有子路由(因此具有插座)和配置了 notFoundComponent。
当 notFoundMode 设置为 root 时,所有未找到错误都将由根路由的 notFoundComponent 处理,而不是从最近的模糊匹配路由冒泡。
例如,考虑以下路由树
如果提供了 /posts/1/edit 的路径,将渲染以下组件结构
__root__ 路由的 notFoundComponent 将被渲染,因为 notFoundMode 设置为 root。
要处理两种类型的未找到错误,您可以将 notFoundComponent 附加到路由。当抛出未找到错误时,将渲染此组件。
例如,为 /settings 路由配置 notFoundComponent 以处理不存在的设置页面
export const Route = createFileRoute('/settings')({
component: () => {
return (
<div>
<p>Settings page</p>
<Outlet />
</div>
)
},
notFoundComponent: () => {
return <p>This setting page doesn't exist!</p>
},
})
export const Route = createFileRoute('/settings')({
component: () => {
return (
<div>
<p>Settings page</p>
<Outlet />
</div>
)
},
notFoundComponent: () => {
return <p>This setting page doesn't exist!</p>
},
})
或者为 /posts/$postId 路由配置 notFoundComponent 以处理不存在的帖子
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params: { postId } }) => {
const post = await getPost(postId)
if (!post) throw notFound()
return { post }
},
component: ({ post }) => {
return (
<div>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
)
},
notFoundComponent: () => {
return <p>Post not found!</p>
},
})
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params: { postId } }) => {
const post = await getPost(postId)
if (!post) throw notFound()
return { post }
},
component: ({ post }) => {
return (
<div>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
)
},
notFoundComponent: () => {
return <p>Post not found!</p>
},
})
您可能希望为应用程序中每个具有子路由的路由提供默认的未找到组件。
为什么只对具有子路由的路由?叶节点路由(没有子路由的路由)永远不会渲染 Outlet,因此无法处理未找到错误。
为此,请将 defaultNotFoundComponent 传递给 createRouter 函数
const router = createRouter({
defaultNotFoundComponent: () => {
return (
<div>
<p>Not found!</p>
<Link to="/">Go home</Link>
</div>
)
},
})
const router = createRouter({
defaultNotFoundComponent: () => {
return (
<div>
<p>Not found!</p>
<Link to="/">Go home</Link>
</div>
)
},
})
您可以使用 notFound 函数在加载器方法和组件中手动抛出未找到错误。当您需要指示找不到资源时,这非常有用。
notFound 函数的工作方式与 redirect 函数类似。要导致未找到错误,您可以抛出 notFound()。
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params: { postId } }) => {
// Returns `null` if the post doesn't exist
const post = await getPost(postId)
if (!post) {
throw notFound()
// Alternatively, you can make the notFound function throw:
// notFound({ throw: true })
}
// Post is guaranteed to be defined here because we threw an error
return { post }
},
})
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params: { postId } }) => {
// Returns `null` if the post doesn't exist
const post = await getPost(postId)
if (!post) {
throw notFound()
// Alternatively, you can make the notFound function throw:
// notFound({ throw: true })
}
// Post is guaranteed to be defined here because we threw an error
return { post }
},
})
上面的未找到错误将由同一路由或最近的父路由处理,该路由或最近的父路由配置了 notFoundComponent 路由选项或 defaultNotFoundComponent 路由器选项。
如果找不到路由或任何合适的父路由来处理错误,则根路由将使用 TanStack Router 的 极其基本(且有意不理想) 默认未找到组件来处理它,该组件仅渲染 <div>Not Found</div>。强烈建议至少将一个 notFoundComponent 附加到根路由,或配置一个路由器范围的 defaultNotFoundComponent 来处理未找到错误。
有时您可能希望在特定的父路由上触发未找到错误,并绕过正常的未找到组件传播。为此,请将路由 ID 传递到 notFound 函数中的 route 选项。
// _pathlessLayout.tsx
export const Route = createFileRoute('/_pathlessLayout')({
// This will render
notFoundComponent: () => {
return <p>Not found (in _pathlessLayout)</p>
},
component: () => {
return (
<div>
<p>This is a pathless layout route!</p>
<Outlet />
</div>
)
},
})
// _pathlessLayout/route-a.tsx
export const Route = createFileRoute('/_pathless/route-a')({
loader: async () => {
// This will make LayoutRoute handle the not-found error
throw notFound({ routeId: '/_pathlessLayout' })
// ^^^^^^^^^ This will autocomplete from the registered router
},
// This WILL NOT render
notFoundComponent: () => {
return <p>Not found (in _pathlessLayout/route-a)</p>
},
})
// _pathlessLayout.tsx
export const Route = createFileRoute('/_pathlessLayout')({
// This will render
notFoundComponent: () => {
return <p>Not found (in _pathlessLayout)</p>
},
component: () => {
return (
<div>
<p>This is a pathless layout route!</p>
<Outlet />
</div>
)
},
})
// _pathlessLayout/route-a.tsx
export const Route = createFileRoute('/_pathless/route-a')({
loader: async () => {
// This will make LayoutRoute handle the not-found error
throw notFound({ routeId: '/_pathlessLayout' })
// ^^^^^^^^^ This will autocomplete from the registered router
},
// This WILL NOT render
notFoundComponent: () => {
return <p>Not found (in _pathlessLayout/route-a)</p>
},
})
您还可以通过将导出的 rootRouteId 变量传递给 notFound 函数的 route 属性来定位根路由
import { rootRouteId } from '@tanstack/react-router'
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params: { postId } }) => {
const post = await getPost(postId)
if (!post) throw notFound({ routeId: rootRouteId })
return { post }
},
})
import { rootRouteId } from '@tanstack/react-router'
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params: { postId } }) => {
const post = await getPost(postId)
if (!post) throw notFound({ routeId: rootRouteId })
return { post }
},
})
您也可以在组件中抛出未找到错误。但是,建议在加载器方法中而不是在组件中抛出未找到错误,以便正确键入加载器数据并防止闪烁。
TanStack Router 公开了一个类似于 CatchBoundary 的 CatchNotFound 组件,该组件可用于捕获组件中的未找到错误并相应地显示 UI。
notFoundComponent
内部加载数据notFoundComponent 在数据加载方面是一种特殊情况。SomeRoute.useLoaderData 可能未定义,具体取决于您尝试访问哪个路由以及未找到错误在何处抛出。但是,Route.useParams、Route.useSearch、Route.useRouteContext 等将返回已定义的值。
如果您需要将不完整的加载器数据传递给 notFoundComponent, 请通过 notFound 函数中的 data 选项传递数据,并在 notFoundComponent 中验证它。
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params: { postId } }) => {
const post = await getPost(postId)
if (!post)
throw notFound({
// Forward some data to the notFoundComponent
// data: someIncompleteLoaderData
})
return { post }
},
// `data: unknown` is passed to the component via the `data` option when calling `notFound`
notFoundComponent: ({ data }) => {
// ❌ useLoaderData is not valid here: const { post } = Route.useLoaderData()
// ✅:
const { postId } = Route.useParams()
const search = Route.useSearch()
const context = Route.useRouteContext()
return <p>Post with id {postId} not found!</p>
},
})
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params: { postId } }) => {
const post = await getPost(postId)
if (!post)
throw notFound({
// Forward some data to the notFoundComponent
// data: someIncompleteLoaderData
})
return { post }
},
// `data: unknown` is passed to the component via the `data` option when calling `notFound`
notFoundComponent: ({ data }) => {
// ❌ useLoaderData is not valid here: const { post } = Route.useLoaderData()
// ✅:
const { postId } = Route.useParams()
const search = Route.useSearch()
const context = Route.useRouteContext()
return <p>Post with id {postId} not found!</p>
},
})
有关更多信息,请参阅 SSR 指南。
NotFoundRoute API 已被弃用,取而代之的是 notFoundComponent。NotFoundRoute API 将在未来版本中移除。
当使用 NotFoundRoute 时,notFound 函数和 notFoundComponent 将不起作用。
主要区别在于
要从 NotFoundRoute 迁移到 notFoundComponent,您只需要进行一些更改
// router.tsx
import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen.'
- import { notFoundRoute } from './notFoundRoute' // [!code --]
export const router = createRouter({
routeTree,
- notFoundRoute // [!code --]
})
// routes/__root.tsx
import { createRootRoute } from '@tanstack/react-router'
export const Route = createRootRoute({
// ...
+ notFoundComponent: () => { // [!code ++]
+ return <p>Not found!</p> // [!code ++]
+ } // [!code ++]
})
// router.tsx
import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen.'
- import { notFoundRoute } from './notFoundRoute' // [!code --]
export const router = createRouter({
routeTree,
- notFoundRoute // [!code --]
})
// routes/__root.tsx
import { createRootRoute } from '@tanstack/react-router'
export const Route = createRootRoute({
// ...
+ notFoundComponent: () => { // [!code ++]
+ return <p>Not found!</p> // [!code ++]
+ } // [!code ++]
})
重要更改
您的每周 JavaScript 新闻速递。每周一免费发送给超过 100,000 名开发者。