Files
supabase/packages/common/hooks/useSearchParamsShallow.ts
Gildas Garcia 86a3f8b03d chore: upgrade to react-19 (#45886)
- Most changes are related to either types or `useRef` usages (it now
requires an initial value).
- also updated `vaul` to its latest version and haven't noticed any
change ([design-system
demo](https://design-system-git-react-19-supabase.vercel.app/design-system/docs/components/drawer))

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Upgraded workspace to React 19.

* **Bug Fixes**
* Improved null-safety and ref handling across editors, UI components,
shortcuts, and markdown/image rendering to reduce runtime errors.
* Safer event/timeout/interval cleanup and more robust command/context
handling.

* **Chores**
  * Bumped vaul dependency versions.

* **Documentation**
* Type and TypeScript accuracy improvements for clearer developer
feedback.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45886)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-15 16:04:41 +02:00

107 lines
3.4 KiB
TypeScript

'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 = 'supabase.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 }