| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- 'use client'
- import { useCallback, useEffect, useId, useMemo, useReducer, useRef } from 'react'
- /**
- * Stores state in search params while bypassing Next Router.
- *
- * The purpose of this is to use search params while maintaining SSG ability,
- * because Next.js's `useSearchParams` forces at least the children to be
- * client-side rendered on static routes.
- *
- * See https://nextjs.org/docs/app/api-reference/functions/use-search-params
- */
- const useSearchParamsShallow = () => {
- const EVENT_NAME = 'briven.events.packages.common.useSearchParamsShallow'
- const id = useId()
- const timeoutHandle = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
- const reducer = useCallback(
- (_: URLSearchParams, action: { target: 'int' | 'ext'; newParams: URLSearchParams }) => {
- clearTimeout(timeoutHandle.current)
- if (action.target === 'ext') {
- /**
- * Doing this in the next tick makes sure that the originating
- * component finishes rendering before it triggers updates to other
- * components.
- */
- timeoutHandle.current = setTimeout(() => {
- document.dispatchEvent(new CustomEvent(EVENT_NAME, { detail: { id } }))
- })
- }
- return action.newParams
- },
- [id]
- )
- useEffect(() => () => clearTimeout(timeoutHandle.current), [])
- const [localParams, setLocalParams] = useReducer(reducer, undefined, () => new URLSearchParams())
- useEffect(() => {
- const handler = (event: CustomEvent<{ id: string }>) => {
- if (event.detail.id !== id) {
- const globalParams = new URLSearchParams(window.location.search)
- setLocalParams({ target: 'int', newParams: globalParams })
- }
- }
- document.addEventListener(EVENT_NAME, handler as EventListener)
- return () => document.removeEventListener(EVENT_NAME, handler as EventListener)
- }, [id])
- useEffect(() => {
- const globalParams = new URLSearchParams(window.location.search)
- setLocalParams({ target: 'int', newParams: globalParams })
- }, [])
- const has = useCallback((key: string) => localParams.has(key), [localParams])
- const get = useCallback((key: string) => localParams.get(key), [localParams])
- const getAll = useCallback((key: string) => localParams.getAll(key), [localParams])
- const set = useCallback((key: string, value: any) => {
- if (typeof window === 'undefined') return
- const url = new URL(window.location.href)
- url.searchParams.set(key, value)
- window.history.replaceState(null, '', url)
- setLocalParams({ target: 'ext', newParams: url.searchParams })
- }, [])
- const append = useCallback((key: string, value: any) => {
- if (typeof window === 'undefined') return
- const url = new URL(window.location.href)
- url.searchParams.append(key, value)
- window.history.replaceState(null, '', url)
- setLocalParams({ target: 'ext', newParams: url.searchParams })
- }, [])
- const _delete = useCallback((key: string) => {
- if (typeof window === 'undefined') return
- const url = new URL(window.location.href)
- url.searchParams.delete(key)
- window.history.replaceState(null, '', url)
- setLocalParams({ target: 'ext', newParams: url.searchParams })
- }, [])
- const api = useMemo(
- () => ({
- has,
- get,
- getAll,
- append,
- set,
- delete: _delete,
- }),
- [has, get, getAll, append, set, _delete]
- )
- return api
- }
- export { useSearchParamsShallow }
|