UNPKG

nuqs-zod-adapter

Version:

A Nuqs adapter to safely bind Zod schemas with query parameters using type safety, debouncing and auto-reset support.

92 lines (77 loc) 2.41 kB
import { useQueryState, parseAsString, parseAsInteger, parseAsBoolean, type Options as BaseOptions } from 'nuqs' import { useDebouncedCallback } from 'use-debounce' import { useEffect, useState } from 'react' import { z } from 'zod' type Options = BaseOptions & { delay?: number resetKeys?: string[] } export function useSafeQueryStateFromZod<S extends z.ZodTypeAny>( key: string, schema: S, options?: Options ): [ z.infer<S>, (value: z.infer<S>) => void ] { let baseSchema = schema let defaultValue: any = undefined if (schema._def.typeName === 'ZodDefault') { defaultValue = schema._def.defaultValue() baseSchema = schema._def.innerType } const typeName = baseSchema._def.typeName let parser: any switch (typeName) { case 'ZodString': case 'ZodEnum': case 'ZodNativeEnum': case 'ZodLiteral': case 'ZodUnion': parser = parseAsString.withDefault(defaultValue) break case 'ZodNumber': parser = parseAsInteger.withDefault(defaultValue) break case 'ZodBoolean': parser = parseAsBoolean.withDefault(defaultValue) break case 'ZodEffects': if (baseSchema._def.schema?._def?.typeName === 'ZodNumber') { parser = parseAsInteger.withDefault(defaultValue) } else { parser = parseAsString.withDefault(defaultValue) } break default: } const [rawValue, setRaw] = useQueryState<z.infer<S>>(key, { ...parser, ...options }) const delay = options?.delay const resetKeys = options?.resetKeys ?? [] if (!delay) return [rawValue ?? defaultValue, setRaw] const [internalValue, setInternalValue] = useState<z.infer<S> | null>(rawValue) const debouncedSet = useDebouncedCallback((v: z.infer<S> | null) => { setRaw(v) for (const rk of resetKeys) { const [, resetSetter] = useQueryState(rk, parseAsString.withDefault('')) resetSetter(null) } }, delay) const set = (v: z.infer<S> | null) => { setInternalValue(v) debouncedSet(v) } useEffect(() => { setInternalValue(rawValue) }, [rawValue]) return [internalValue ?? defaultValue as z.infer<S>, set] }