🏖️ TanStack Router:搜索参数即状态!🚀🚀🚀

前言

之前介绍过 TanStack,最近发现 TanStack Router 更新了一篇博客,介绍了搜索参数解析的新理念!今天分享给大家~,下面是翻译之后的原文!

往期精彩推荐

正文

搜索参数即状态

搜索参数历来被视为二等状态。它们是全局的、可序列化的、可共享的------但在大多数应用程序中,它们仍然通过字符串解析、松散的约定和脆弱的实用工具拼凑而成。

即使是像验证 sort 参数这样简单的事情,也很快变得冗长:

ts 复制代码
const schema = z.object({
  sort: z.enum(['asc', 'desc']),
})

const raw = Object.fromEntries(new URLSearchParams(location.href))
const result = schema.safeParse(raw)

if (!result.success) {
  // 回退、重定向或显示错误
}

这种方法有效,但它是手动的且重复的。没有类型推断、与路由本身没有关联,而且一旦你想添加更多类型、默认值、转换或结构,它就会崩溃。

更糟糕的是,URLSearchParams 只支持字符串。它不支持嵌套 JSON、数组(除了简单的逗号分割外)或类型强制转换。因此,除非你的状态是扁平且简单的,否则你很快就会遇到瓶颈。

这就是为什么我们开始看到工具和提案的兴起------比如 Nuqs、Next.js RFCs 和用户自定义模式------旨在使搜索参数更具类型安全性和人体工程学。这些大多专注于改进从 URL 的 读取

但几乎没有哪一个解决了更深层次、更困难的问题:写入 搜索参数,以安全和原子化的方式,充分了解路由上下文。

写入搜索参数是问题所在

从 URL 读取是一回事。从代码中构建一个有效且有意的 URL 是另一回事。

当你尝试这样做时:

tsx 复制代码
<Link to="/dashboards/overview" search={{ sort: 'asc' }} />

你会意识到你根本不知道这个路由支持哪些搜索参数,或者你是否正确地格式化了它们。即使有助手来将它们字符串化,也没有任何机制来强制调用者和路由之间的契约。没有类型推断、没有验证、没有护栏。

这就是 约束成为特性 的地方。

如果不在路由本身中明确声明搜索参数模式,你就只能猜测。你可能在一个地方进行了验证,但没有什么能阻止另一个组件使用无效、部分或冲突的状态进行导航。

约束是协调成为可能的关键。它使 非本地调用者 能够安全参与。

本地抽象有所帮助 ------ 但它们无法协调

Nuqs 这样的工具是本地抽象如何改善搜索参数处理 人体工程学 的绝佳例子。你可以获得基于 Zod 的解析、类型推断,甚至是可写的 API------所有这些都限定在特定组件或钩子中。

它们使在 隔离 中读写搜索参数变得更容易------这很有价值。

但它们无法解决更广泛的 协调 问题。你仍然会遇到重复的模式、分散的期望,以及无法在路由或组件之间强制一致性的问题。默认值可能冲突。类型可能漂移。当路由演变时,没有什么能保证所有调用者都会随之更新。

这才是真正的碎片化问题------解决它需要将搜索参数模式引入路由层本身。

TanStack Router 如何解决这个问题

TanStack Router 提供了整体解决方案。

你无需在应用程序中分散模式逻辑,而是 在路由本身中定义它

ts 复制代码
export const Route = createFileRoute('/dashboards/overview')({
  validateSearch: z.object({
    sort: z.enum(['asc', 'desc']),
    filter: z.string().optional(),
  }),
})

这个模式成为唯一的真相来源。你在任何地方都能获得完整的推断、验证和自动补全:

tsx 复制代码
<Link
  to="/dashboards/overview"
  search={{ sort: 'asc' }} // 完全类型化,完全验证
/>

想只更新部分搜索状态?没问题:

ts 复制代码
navigate({
  search: (prev) => ({ ...prev, page: prev.page + 1 }),
})

它是 reducer 风格的、事务性的,并直接与路由器的响应性模型集成。组件仅在它们使用的特定搜索参数发生变化时才会重新渲染------而不是每次 URL 发生变化时。

TanStack Router 如何防止模式碎片化

当你的搜索参数逻辑存在于用户空间------分散在钩子、实用工具和助手函数中------不可避免地会出现 冲突的模式

也许一个组件期望 sort: 'asc' | 'desc'。另一个添加了 filter。第三个假定 sort: 'desc' 为默认值。它们之间没有共享的真相来源。

这会导致:

  • 不一致的默认值
  • 冲突的格式
  • 设置了其他组件无法解析的值的导航
  • 损坏的深度链接和无法追踪的错误

TanStack Router 通过将模式直接绑定到路由定义------以 层级方式 防止这种情况。

父路由可以定义共享的搜索参数验证。子路由继承该上下文,以类型安全的方式添加或扩展它。这使得在应用程序的不同部分意外创建重叠、不兼容的模式变得 不可能

示例:安全的分层搜索参数验证

以下是实际操作方式:

ts 复制代码
// routes/dashboard.tsx
export const Route = createFileRoute('/dashboard')({
  validateSearch: z.object({
    sort: z.enum(['asc', 'desc']).default('asc'),
  }),
})

然后,子路由可以安全地扩展该模式:

ts 复制代码
// routes/dashboard/$dashboardId.tsx
export const Route = createFileRoute('/dashboard/$dashboardId')({
  validateSearch: z.object({
    filter: z.string().optional(),
    // ✅ `sort` 从父路由自动继承
  }),
})

当你匹配 /dashboard/123?sort=desc&filter=active 时,父路由验证 sort,子路由验证 filter,一切无缝协作。

尝试在子路由中将所需的父参数重新定义为完全不同的内容?会触发类型错误。

ts 复制代码
validateSearch: z.object({
  // ❌ 类型错误:布尔值无法扩展父路由的 'asc' | 'desc'
  sort: z.boolean(),
  filter: z.string().optional(),
})

这种强制执行使嵌套路由既可组合又安全------这是一种罕见的组合。

内置纪律

这里的魔法在于,你无需教导团队遵循约定。路由 拥有 模式。每个人只需使用它。没有重复。没有漂移。没有无声的错误。没有猜测。

当你将验证、类型化和所有权引入路由器本身时,你就不再将 URL 当作字符串,而是开始将其视为真正的状态------因为它们就是状态。

搜索参数即状态

大多数路由系统将搜索参数视为事后补充。你 可以 读取它们,也许可以解析,也许可以字符串化,但很少有你真正可以 信任 的东西。

TanStack Router 颠倒了这一观念。它使搜索参数成为路由契约的核心部分------经过验证、可推断、可写且具有响应性。

因为如果你不将搜索参数视为状态,你就会不断泄露它、破坏它并绕过它。

最好从一开始就正确对待它。

如果你对将搜索参数作为一等状态的可能性感到好奇,我们邀请你尝试 TanStack Router。体验在路由逻辑中验证、可推断和响应性搜索参数的力量。

最后

搜索参数很可能导致类型安全和协调等问题。TanStack Router 提供了一种解决方案,将搜索参数模式整合到路由定义中,实现验证、推断和响应性,从而防止模式碎片化并确保安全的分层验证!

原文链接:tanstack.com/blog/search...

往期精彩推荐

相关推荐
xd000023 分钟前
9.axios底层原理,和promise的对比(2)
vue.js
Dignity_呱12 分钟前
别在傻傻分不清any void never unknown的场景啦
前端·vue.js·typescript
crary,记忆34 分钟前
Angular报错:cann‘t bind to ngClass since it is‘t a known property of div
前端·javascript·angular·angular.js
betterangela36 分钟前
react私有样式处理
前端·react.js·前端框架
几何心凉36 分钟前
如何处理React中表单的双向数据绑定?
前端·javascript·react.js
摸鱼仙人~38 分钟前
React Hooks 指南:何时使用 useEffect ?
javascript·react.js·ecmascript
巴巴_羊42 分钟前
React 新项目
前端·react.js·前端框架
Luffe船长1 小时前
elementUI点击浏览table所选行数据查看文档
javascript·elementui·vue
入行IT两年半1 小时前
element-plus 单选组件 el-radio,选不上,又没报错,直接复制官网也不行解决方案
前端·vue.js·elementui
waterHBO1 小时前
一个小小的 flask app, 几个小工具,拼凑一下
javascript·vscode·python·flask·web app·agent mode·vibe coding