TanStackT
TanStack9mo ago
2 replies
incredible-moccasin

useSearchParam custom hook: looking for advice

I'm working on a custom hook useSearchParam which would operate like a useState, but update the search params on change with optional debounce.

I'm looking for the right way to use the built in type-safety for searchParams. But I can't seem to find any way to make it work by passing the Route, RouteContext, searchParams object or anything else from the Route without running into absolute Typescript nightmares.

Current solution: use the inferred type from my Zod schema that's already validating searchParams as a generic that's passed to the hook. It works, but it feels like I'm not using the router as intended.

Is there a better way to do this? And more generally @Tanner Linsley - what's the right way to pass type-safe properties of a Route (like searchParams) to a custom hook that will be reused with many routes?

import { useRouter } from '@tanstack/react-router'
import { useEffect, useState } from 'react'

type UseSearchParamProps<ValueType, SearchParamsType> = {
  key: keyof SearchParamsType
  initial_value?: ValueType
  debounce_time?: number
}

export function useSearchParam<ValueType, SearchParamsType>({
  key,
  initial_value,
  debounce_time = 0
}: UseSearchParamProps<ValueType, SearchParamsType>) {
  const [value, setValue] = useState<ValueType | undefined>(initial_value)
  const { navigate } = useRouter()

  useEffect(() => {
    const debounceTimer = setTimeout(() => {
      navigate({
        replace: true,
        to: '.',
        search: prev => ({ ...prev, [key]: value || undefined })
      })
    }, debounce_time)

    return () => clearTimeout(debounceTimer)
  }, [value, debounce_time, navigate, key])

  return [value, setValue] as const
}
Was this page helpful?