"use client"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { useCallback, useMemo } from "react"; /** URL search params as state: `set({ key: value | null })` replaces the URL (no scroll, no history entry). */ export function useUrlState(): [Record, (patch: Record, opts?: { push?: boolean }) => void] { const sp = useSearchParams(); const router = useRouter(); const pathname = usePathname(); const params = useMemo(() => { const out: Record = {}; sp.forEach((v, k) => { out[k] = v; }); return out; }, [sp]); const set = useCallback( (patch: Record, opts?: { push?: boolean }) => { const next = new URLSearchParams(sp.toString()); for (const [k, v] of Object.entries(patch)) { if (v === null || v === undefined || v === "" || v === false) next.delete(k); else next.set(k, v === true ? "1" : String(v)); } const qs = next.toString(); const url = qs ? `${pathname}?${qs}` : pathname; if (opts?.push) router.push(url, { scroll: false }); else router.replace(url, { scroll: false }); }, [sp, router, pathname], ); return [params, set]; }