内置解析器
当字符串不够用时
搜索参数默认是字符串,但你的状态很可能比这更复杂。
你可能想要使用数字、布尔值、Date、对象、数组,甚至自定义类型。 这就是解析器派上用场的地方。
nuqs 为最常见类型提供了内置解析器,并允许你定义自己的。
字符串
import { parseAsString } from 'nuqs'类型安全提示
parseAsString 是一个空操作:它在解析时不执行任何验证,
并会接受任何值。
如果你期望某些特定的字符串值,例如 'foo' | 'bar',
请参阅字面量 以确保类型运行时安全。
如果搜索参数默认就是字符串,这个_“解析器”_ 有什么意义?
如果声明搜索参数对象,和/或想要使用构建器模式来指定默认值 和选项,它就会变得有用:
export const searchParamsParsers = {
q: parseAsString.withDefault('').withOptions({
shallow: false
})
}数字
整数
使用 parseInt(基数 10)将搜索参数字符串转换为整数。
import { parseAsInteger } from 'nuqs'
useQueryState('int', parseAsInteger.withDefault(0))浮点数
与整数相同,但底层使用 parseFloat。
import { parseAsFloat } from 'nuqs'
useQueryState('float', parseAsFloat.withDefault(0))十六进制
以十六进制编码整数。
import { parseAsHex } from 'nuqs'
useQueryState('hex', parseAsHex.withDefault(0x00))进一步探索
查看 Hex Colors playground 以获取演示。
索引
与整数相同,但对序列化的查询字符串添加 +1 偏移(解析时为 -1)。
适用于分页索引。
import { parseAsIndex } from 'nuqs'
const [pageIndex] = useQueryState('page', parseAsIndex.withDefault(0))布尔值
import { parseAsBoolean } from 'nuqs'
useQueryState('bool', parseAsBoolean.withDefault(false))字面量
这些解析器扩展了基本的整数和浮点解析器,但会针对一些预期值进行验证,这些值定义为 TypeScript 字面量。
import { parseAsStringLiteral, type inferParserType } from 'nuqs'
// 创建解析器
const parser = parseAsStringLiteral(['asc', 'desc'])
// 可选:提取类型
type SortOrder = inferParserType<typeof parser> // 'asc' | 'desc'我应该在解析器内联声明值还是在外部?
这取决于®。在解析器内联声明更简短,并使解析器
成为使用 inferParserType 进行类型推断的真实来源,
但它会将值锁定在解析器内部。
在外部声明允许在运行时读取和迭代这些值。
不过,不要忘记添加 as const,否则类型会扩展为 string。
字符串字面量
import { parseAsStringLiteral } from 'nuqs'
// 列出接受的值
const sortOrder = ['asc', 'desc'] as const
// 然后将其传递给解析器
parseAsStringLiteral(sortOrder)数字字面量
import { parseAsNumberLiteral } from 'nuqs'
parseAsNumberLiteral([1, 2, 3, 4, 5, 6])import { parseAsNumberLiteral } from 'nuqs'
// 列出接受的值
const diceSides = [1, 2, 3, 4, 5, 6] as const
// 然后将其传递给解析器
parseAsNumberLiteral(diceSides)枚举
字符串枚举比字符串字面量稍显冗长,但 nuqs 支持它们。
enum Direction {
up = 'UP',
down = 'DOWN',
left = 'LEFT',
right = 'RIGHT'
}
parseAsStringEnum<Direction>(Object.values(Direction))注意
查询字符串的值将是枚举的值,而不是其名称(此处:
?direction=UP)。
日期 & 时间戳
有三种解析器可以给你一个 Date 对象,它们的区别在于
如何将值编码到查询字符串中。
ISO 8601 日期时间
import { parseAsIsoDateTime } from 'nuqs'ISO 8601 日期
import { parseAsIsoDate } from 'nuqs'日期在不带时区偏移的情况下解析,使其为 UTC 的 00:00:00。
时间戳
自 Unix 纪元以来的毫秒数。
import { parseAsTimestamp } from 'nuqs'数组
本页上的所有解析器都可以用于解析各自类型的数组。
import { parseAsArrayOf, parseAsInteger } from 'nuqs'
parseAsArrayOf(parseAsInteger)
// 可选:自定义分隔符
parseAsArrayOf(parseAsInteger, ';')JSON
如果基本类型不够用,你可以将 JSON 编码到查询字符串中。
传递一个 Standard Schema(例如:Zod 模式) 来验证和推断解析数据的类型:
import { parseAsJson } from 'nuqs'
import { z } from 'zod'
const schema = z.object({
pkg: z.string(),
version: z.number(),
worksWith: z.array(z.string())
})
// 这个解析器是一个函数,不要忘记调用它
// 并将你的模式作为参数传递。
const [json, setJson] = useQueryState('json', parseAsJson(schema))
setJson({
pkg: 'nuqs',
version: 2,
worksWith: ['Next.js', 'React', 'Remix', 'React Router', 'and more']
})使用其他验证库也是可能的:parseAsJson 接受
任何兼容 Standard Schema 的输入(例如:ArkType、Valibot),
或自定义验证函数(例如:Yup、Joi 等):
import { object, string, number } from 'yup';
let userSchema = object({
name: string().required(),
age: number().required().positive().integer(),
});
parseAsJson(userSchema.validateSync)注意
验证函数必须抛出错误或
对于无效数据返回 null。仅支持同步验证。
原生数组
如果你想使用原生 URL 格式的数组,像这样重复相同的键多次:
/products?tag=books&tag=tech &tag=design
现在你可以使用 MultiParsers 如 parseAsNativeArrayOf 来以完全类型安全的方式读取和写入这些值。
import { useQueryState, parseAsNativeArrayOf, parseAsInteger } from 'nuqs'
const [projectIds, setProjectIds] = useQueryState(
'project',
parseAsNativeArrayOf(parseAsInteger)
)
// ?project=123&project=456 → [123, 456]注意:空数组默认值
parseAsNativeArrayOf 有一个内置的空数组默认值(.withDefault([])),这样你就不必处理 null 情况。
.withDefault() 的调用可以链式使用,因此你可以用它来设置自定义默认值。
在服务器端使用解析器
对于可能在 Next.js app router 中导入的共享代码,你应该从 nuqs/server 导入
解析器,以便在服务器和客户端代码中使用它们,
因为它不包含 'use client' 指令。
import { parseAsString } from 'nuqs/server'从 nuqs 导入仅在客户端代码中有效,并且在使用函数(如 .withDefault 和 .withOptions)
跨越共享代码时会抛出打包错误。
对于所有其他框架,你可以互换使用它们,因为它们
不关心 'use client' 指令。