用途
TanStack Router 将搜索参数(Search Params)视为 JSON 优先的应用状态。它们会自动从 URL 解析为结构化对象(数字、布尔值、数组、嵌套对象),并在每个路由上通过 validateSearch 进行验证。
主要能力
参数验证
支持通过 Zod、Valibot、ArkType 适配器进行搜索参数验证。
使用 Zod 适配器(推荐)时,需要安装:
npm install zod @tanstack/zod-adapter
示例 schema:
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'
const productSearchSchema = z.object({
page: z.number().default(1).catch(1),
filter: z.string().default(''),
sort: z.enum(['newest', 'oldest', 'price']).default('newest').catch('newest'),
})
export const Route = createFileRoute('/products')({
validateSearch: productSearchSchema,
component: ProductsPage,
})
注意:使用 zodValidator() 与 Zod v3 时,应使用 @tanstack/zod-adapter 的 fallback(),而不是 zod 的 .catch()。使用 .catch() 配合 zod 适配器会让输出类型变为 unknown,破坏类型安全。这不适用于 Valibot 或 ArkType(它们使用各自的回退机制),也不适用于 Zod v4(v4 应使用 .catch(),而不是 zodValidator())。类型可完全推断,不要为 useSearch() 的返回值添加类型标注。
读取搜索参数
路由组件中:Route.useSearch()
function ProductsPage() {
const { page, sort } = Route.useSearch()
return <div>Page {page}</div>
}
代码分割组件中:getRouteApi()
import { getRouteApi } from '@tanstack/react-router'
const routeApi = getRouteApi('/products')
function ProductFilters() {
const { sort } = routeApi.useSearch()
return <select value={sort}>{/* options */}</select>
}
任意组件中:useSearch({ from })
import { useSearch } from '@tanstack/react-router'
function SortIndicator() {
const { sort } = useSearch({ from: '/products' })
return <span>Sorted by: {sort}</span>
}
宽松访问:useSearch({ strict: false })
function GenericPaginator() {
const search = useSearch({ strict: false })
return <span>Page: {search.page ?? 1}</span>
}
写入搜索参数
Link 函数形式(保留已有参数):
<Link from="/products" search={(prev) => ({ ...prev, page: prev.page + 1 })}>
Next Page
</Link>
Link 对象形式(替换所有参数):
<Link to="/products" search={{ page: 1, filter: '', sort: 'newest' }}>
Reset
</Link>
编程式:useNavigate()
const navigate = useNavigate({ from: '/products' })
navigate({
search: (prev) => ({ ...prev, sort: e.target.value, page: 1 }),
})
搜索参数继承
父路由的搜索参数会自动合并到子路由。父路由必须定义 validateSearch,子路由才能继承。
搜索中间件
retainSearchParams:跨导航保留参数。
search: {
middlewares: [retainSearchParams(['debug'])],
}
stripSearchParams:从 URL 中移除默认值。
search: {
middlewares: [stripSearchParams(defaults)],
}
中间件可以链式组合:
search: {
middlewares: [
retainSearchParams(['retainMe']),
stripSearchParams({ arrayWithDefaults: ['foo', 'bar'] }),
],
}
自定义序列化
可在路由器级别覆盖默认的 JSON 序列化:
import {
createRouter,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/react-router'
const router = createRouter({
routeTree,
parseSearch: parseSearchWith(parse),
stringifySearch: stringifySearchWith(stringify),
})
通过 loaderDeps 在 loader 中使用
export const Route = createFileRoute('/products')({
validateSearch: productSearchSchema,
loaderDeps: ({ search }) => ({ page: search.page }),
loader: async ({ deps }) => {
return fetchProducts({ page: deps.page })
},
})
常见错误
-
在 Zod v3 中将 .catch() 与 zodValidator() 一起使用,而非适配器的 fallback()。正确做法是导入 fallback 并使用
fallback(z.number(), 1)。此问题仅适用于 Zod v3。 -
从 loaderDeps 返回整个搜索对象 —— 会导致任何搜索参数变化都触发 loader 重新运行。应只挑选 loader 需要的参数。
-
在搜索参数中传递 Date 对象 —— Date 无法正确序列化为 URL 中的 JSON。应转换为 ISO 字符串:
new Date().toISOString()。 -
父路由缺少 validateSearch 会阻止继承。
-
将 search 用作对象而非函数会丢失参数 —— 应使用函数形式
search={(prev) => ({ ...prev, page: 2 })}保留现有参数。
适用场景与限制
适用于使用 TanStack Router 的 React 应用,需要在 URL 中管理结构化、类型安全的搜索参数状态,并执行验证、默认值处理、参数保留或清理。限制与更多模式信息暂未提供。