迁移到 v2 的指南
如何更新您的代码以使用 nuqs@2.0.0
这是 nuqs@2.0.0 中的重大变更总结:
- 通过 adapters 启用对其他 React 框架的支持
- 行为变更
- 仅 ESM 包
- 已移除已弃用的导出
- 将
nuqs/parsers重命名为nuqs/server - 调试输出检测
- 放弃
next-usequerystate - 类型变更
Adapters
最大的变化是 nuqs@2.0.0 现在支持其他 React 框架,
为所有框架提供类型安全的 URL 状态。
您需要用适用于您的框架或路由器的适当 adapter 包装您的应用,以让钩子知道如何与之交互。
当前可用的适配器包括:
- Next.js(app 和 pages 路由器)
- React SPA
- Remix
- React Router
- 测试环境(Vitest、Jest 等)
如果您是从仅支持 Next.js 的 nuqs v1 迁移,您需要
用适当的 NuqsAdapter 包装您的应用:
Next.js
最低要求版本:next@>=14.2.0
Next.js 14 的早期版本在浅路由方面处于变动中。
支持那些早期版本需要大量黑客技巧、工作绕道和
性能惩罚,这些在 nuqs@2.0.0 中已被移除。
App 路由器
import { NuqsAdapter } from 'nuqs/adapters/next/app'
import { type ReactNode } from 'react'
export default function RootLayout({
children
}: {
children: ReactNode
}) {
return (
<html>
<body>
<NuqsAdapter>{children}</NuqsAdapter>
</body>
</html>
)
}Pages 路由器
import type { AppProps } from 'next/app'
import { NuqsAdapter } from 'nuqs/adapters/next/pages'
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<NuqsAdapter>
<Component {...pageProps} />
</NuqsAdapter>
)
}统一(路由无关)
如果您的 Next.js 应用同时使用 app 和 pages 路由器,并且适配器需要 在其中一个中挂载,您可以导入统一适配器,但会以 略微增加捆绑包大小(~100B)为代价。
import { NuqsAdapter } from 'nuqs/adapters/next'其他适配器
虽然不是从 v1 迁移的一部分,但您现在可以通过各自的 adapters 在其他 React 框架中使用 nuqs。
然而,还有一个可能对您感兴趣的适配器,它解决了 使用 nuqs 钩子测试组件的长期问题:
测试适配器
单元测试使用 nuqs v1 的组件很麻烦,因为它需要模拟 Next.js 路由器内部,导致抽象泄漏。
在 v2 中,您现在可以用 NuqsTestingAdapter 包装您的组件进行测试,
它提供了方便的设置和断言 API,用于您的测试。
以下是使用 Vitest 和 Testing Library 的示例:
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { NuqsTestingAdapter, type UrlUpdateEvent } from 'nuqs/adapters/testing'
import { describe, expect, it, vi } from 'vitest'
import { CounterButton } from './counter-button'
it('should increment the count when clicked', async () => {
const user = userEvent.setup()
const onUrlUpdate = vi.fn<[UrlUpdateEvent]>()
render(<CounterButton />, {
// Setup the test by passing initial search params / querystring:
wrapper: ({ children }) => (
<NuqsTestingAdapter searchParams="?count=1" onUrlUpdate={onUrlUpdate}>
{children}
</NuqsTestingAdapter>
)
})
// Act
const button = screen.getByRole('button')
await user.click(button)
// Assert changes in the state and in the (mocked) URL
expect(button).toHaveTextContent('count is 2')
expect(onUrlUpdate).toHaveBeenCalledOnce()
expect(onUrlUpdate.mock.calls[0][0].queryString).toBe('?count=2')
expect(onUrlUpdate.mock.calls[0][0].searchParams.get('count')).toBe('2')
expect(onUrlUpdate.mock.calls[0][0].options.history).toBe('push')
})Behaviour changes
设置 startTransition 选项不再自动设置 shallow: false。
这是为了与其他没有
浅/深路由概念的框架保持一致。
您需要同时设置两者,以继续向服务器发送更新并在 Next.js 中获取加载 状态:
useQueryState('q', {
startTransition: true,
+ shallow: false
})"use client" 指令未包含在客户端导入
(import {} from 'nuqs')中。现在已添加,这意味着服务器端代码
需要从 nuqs/server 导入,以避免如下错误:
Error: Attempted to call withDefault() from the server but withDefault is on
the client. It's not possible to invoke a client function from the server, it can
only be rendered as a Component or passed to props of a Client
Component.ESM only
nuqs@2.0.0 现在是一个 ESM-only
包。这应该不是大问题,因为 Next.js 从版本 12 开始支持 ESM 在
应用代码中使用,但如果您将 nuqs 代码捆绑到一个
中间 CJS 库中以供 Next.js 使用,您会遇到导入问题:
[ERR_REQUIRE_ESM]: require() of ES Module not supported如果将您的库转换为 ESM 不可行,您的主要选项是
动态导入 nuqs:
const { useQueryState } = await import('nuqs')Deprecated exports
v1 API 中的一些部分早在 2023 年 9 月就被标记为已弃用,并在
nuqs@2.0.0 中移除。
queryTypes parsers 对象
queryTypes 对象已被移除,转而使用单个解析器导出,
以实现更好的 tree-shaking。
用 parseAsXYZ 替换以匹配:
- import { queryTypes } from 'nuqs'
+ import { parseAsString, parseAsInteger, ... } from 'nuqs'
- useQueryState('q', queryTypes.string.withOptions({ ... }))
- useQueryState('page', queryTypes.integer.withDefault(1))
+ useQueryState('q', parseAsString.withOptions({ ... }))
+ useQueryState('page', parseAsInteger.withDefault(1))subscribeToQueryUpdates
Next.js 14.1.0 使 useSearchParams 对浅搜索参数更新具有响应性,
这使得这个内部辅助函数变得多余。参见 #425 以获取上下文。
Renamed nuqs/parsers to nuqs/server
在引入服务器缓存 #397 时,专用的解析器导出被
重用,因为它不包含 "use client" 指令。由于它现在包含
不止解析器,并且将来可能会扩展为服务器端代码,
它已被重命名为更清晰的导出名称。
在您的代码中查找并替换所有 nuqs/parsers 的出现为 nuqs/server:
- import { parseAsInteger, createSearchParamsCache } from 'nuqs/parsers'
+ import { parseAsInteger, createSearchParamsCache } from 'nuqs/server'Debug printout detection
重命名为 nuqs 后,调试输出检测逻辑处理
localStorage.debug 变量中存在 next-usequerystate 或 nuqs。
nuqs@2.0.0 仅检查 nuqs 子串的存在以启用日志。
通过在开发工具控制台中运行以下代码一次,来更新您的本地开发环境以匹配:
if (localStorage.debug) {
localStorage.debug = localStorage.debug.replace('next-usequerystate', 'nuqs')
}Dropping next-usequerystate
此包最初名为 next-usequerystate,并于 2024 年 1 月
重命名为 nuqs。旧包名作为 v1 发布线的别名保留。
nuqs 版本 2 及更高版本不再镜像到 next-usequerystate 包名。
Type changes
以下重大变更仅适用于导出的类型:
Options类型不再是泛型的UseQueryStatesOptions现在是一个类型而不是接口,并且现在 是您传递给useQueryStates的对象的泛型。parseAsJson现在需要一个运行时 验证函数来推断解析的 JSON 数据类型。