useAppsQueryState.ts 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
  1. import { type ReadonlyURLSearchParams, usePathname, useRouter, useSearchParams } from 'next/navigation'
  2. import { useCallback, useEffect, useMemo, useState } from 'react'
  3. type AppsQuery = {
  4. tagIDs?: string[]
  5. keywords?: string
  6. }
  7. // Parse the query parameters from the URL search string.
  8. function parseParams(params: ReadonlyURLSearchParams): AppsQuery {
  9. const tagIDs = params.get('tagIDs')?.split(';')
  10. const keywords = params.get('keywords') || undefined
  11. return { tagIDs, keywords }
  12. }
  13. // Update the URL search string with the given query parameters.
  14. function updateSearchParams(query: AppsQuery, current: URLSearchParams) {
  15. const { tagIDs, keywords } = query || {}
  16. if (tagIDs && tagIDs.length > 0)
  17. current.set('tagIDs', tagIDs.join(';'))
  18. else
  19. current.delete('tagIDs')
  20. if (keywords)
  21. current.set('keywords', keywords)
  22. else
  23. current.delete('keywords')
  24. }
  25. function useAppsQueryState() {
  26. const searchParams = useSearchParams()
  27. const [query, setQuery] = useState<AppsQuery>(() => parseParams(searchParams))
  28. const router = useRouter()
  29. const pathname = usePathname()
  30. const syncSearchParams = useCallback((params: URLSearchParams) => {
  31. const search = params.toString()
  32. const query = search ? `?${search}` : ''
  33. router.push(`${pathname}${query}`)
  34. }, [router, pathname])
  35. // Update the URL search string whenever the query changes.
  36. useEffect(() => {
  37. const params = new URLSearchParams(searchParams)
  38. updateSearchParams(query, params)
  39. syncSearchParams(params)
  40. }, [query, searchParams, syncSearchParams])
  41. return useMemo(() => ({ query, setQuery }), [query])
  42. }
  43. export default useAppsQueryState