Parsers
自定义解析器
为自定义数据类型和美观 URL 创建自己的解析器
您可能希望自定义数据类型的渲染查询字符串。
为此,nuqs 提供了 createParser 函数来创建自己的解析器。
您需要传递两个函数:
parse:一个函数,它接受一个字符串并返回解析后的值,如果无效则返回null。serialize:一个函数,它接受解析后的值并返回一个字符串。
import { createParser } from 'nuqs'
const parseAsStarRating = createParser({
parse(queryValue) {
const inBetween = queryValue.split('★')
const isValid = inBetween.length > 1 && inBetween.every(s => s === '')
if (!isValid) return null
const numStars = inBetween.length - 1
return Math.min(5, numStars)
},
serialize(value) {
return Array.from({length: value}, () => '★').join('')
}
})等式函数
对于无法通过 === 操作符比较的状态类型,您还需要提供一个 eq 函数:
// Eg: TanStack Table sorting state
// /?sort=foo:asc → { id: 'foo', desc: false }
const parseAsSort = createParser({
parse(query) {
const [key = '', direction = ''] = query.split(':')
const desc = parseAsStringLiteral(['asc', 'desc']).parse(direction) ?? 'asc'
return {
id: key,
desc: desc === 'desc'
}
},
serialize(value) {
return `${value.id}:${value.desc ? 'desc' : 'asc'}`
},
eq(a, b) {
return a.id === b.id && a.desc === b.desc
}
})此函数用于 clearOnDefault 选项,
以检查当前值是否等于默认值。
自定义多值解析器
我们之前看到的解析器是 SingleParsers:它们操作 URL 中键的第一个出现位置,并且在可用时为您提供一个字符串值来解析。
MultiParsers 的工作方式类似于 SingleParsers,但它们操作数组,以支持键重复:
/?tag=type-safe&tag=url-state &tag=react
这意味着:
parse接受一个Array<string>。它接收操作键的所有匹配值,并返回解析后的值,如果无效则返回null。serialize接受解析后的值并返回一个Array<string>,其中每个项目将单独添加到 URL 中。
然后,您可以将此数组组合并归约以形成复杂数据类型:
/**
* 100~200 <=> { gte: 100, lte: 200 }
* 150 <=> { eq: 150 }
*/
const parseAsFromTo = createParser({
parse: value => {
const [min = null, max = null] = value.split('~').map(parseAsInteger.parse)
if (min === null) return null
if (max === null) return { eq: min }
return { gte: min, lte: max }
},
serialize: value => {
return value.eq !== undefined ? String(value.eq) : `${value.gte}~${value.lte}`
}
})
/**
* foo:bar <=> { key: 'foo', value: 'bar' }
*/
const parseAsKeyValue = createParser({
parse: value => {
const [key, val] = value.split(':')
if (!key || !val) return null
return { key, value: val }
},
serialize: value => {
return `${value.key}:${value.value}`
}
})
const parseAsFilters = <TItem extends {}>(itemParser: SingleParser<TItem>) => {
return createMultiParser({
parse: values => {
const keyValue = values.map(parseAsKeyValue.parse).filter(v => v !== null)
const result = Object.fromEntries(
keyValue.flatMap(({ key, value }) => {
const parsedValue: TItem | null = itemParser.parse(value)
return parsedValue === null ? [] : [[key, parsedValue]]
})
)
return Object.keys(result).length === 0 ? null : result
},
serialize: values => {
return Object.entries(values).map(([key, value]) => {
if (!itemParser.serialize) return null
return parseAsKeyValue.serialize({ key, value: itemParser.serialize(value) })
}).filter(v => v !== null)
}
})
}
const [filters, setFilters] = useQueryState(
'filters',
parseAsFilters(parseAsFromTo).withDefault({})
)注意事项:有损序列化器
如果您的序列化器丢失精度或无法准确表示底层状态值,那么在重新加载页面或从 URL 恢复状态(例如:在导航时)时,您将丢失此精度。
示例:
const geoCoordParser = {
parse: parseFloat,
serialize: v => v.toFixed(4) // Loses precision
}
const [lat, setLat] = useQueryState('lat', geoCoordParser)在这里,将纬度设置为 1.23456789 将渲染 URL 查询字符串
为 lat=1.2345,而内部 lat 状态将正确设置为
1.23456789。
重新加载页面后,状态将错误地设置为 1.2345。