rsnext/packages/next/client/components/navigation.ts
Tim Neutkens 482e3fbffa
Change useSearchParams to URLSearchParams (#40978)
Similar to #40872 `useSearchParams` now returns a `URLSearchParams` instance instead of a plain object.


## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have a helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-10-15 19:16:50 +00:00

119 lines
3.8 KiB
TypeScript

// useLayoutSegments() // Only the segments for the current place. ['children', 'dashboard', 'children', 'integrations'] -> /dashboard/integrations (/dashboard/layout.js would get ['children', 'dashboard', 'children', 'integrations'])
import { useContext, useMemo } from 'react'
import {
SearchParamsContext,
// ParamsContext,
PathnameContext,
// LayoutSegmentsContext,
} from './hooks-client-context'
import {
AppRouterContext,
LayoutRouterContext,
} from '../../shared/lib/app-router-context'
export {
ServerInsertedHTMLContext,
useServerInsertedHTML,
} from '../../shared/lib/server-inserted-html'
const INTERNAL_URLSEARCHPARAMS_INSTANCE = Symbol(
'internal for urlsearchparams readonly'
)
function readonlyURLSearchParamsError() {
return new Error('ReadonlyURLSearchParams cannot be modified')
}
class ReadonlyURLSearchParams {
[INTERNAL_URLSEARCHPARAMS_INSTANCE]: URLSearchParams
entries: URLSearchParams['entries']
forEach: URLSearchParams['forEach']
get: URLSearchParams['get']
getAll: URLSearchParams['getAll']
has: URLSearchParams['has']
keys: URLSearchParams['keys']
values: URLSearchParams['values']
toString: URLSearchParams['toString']
constructor(urlSearchParams: URLSearchParams) {
// Since `new Headers` uses `this.append()` to fill the headers object ReadonlyHeaders can't extend from Headers directly as it would throw.
this[INTERNAL_URLSEARCHPARAMS_INSTANCE] = urlSearchParams
this.entries = urlSearchParams.entries.bind(urlSearchParams)
this.forEach = urlSearchParams.forEach.bind(urlSearchParams)
this.get = urlSearchParams.get.bind(urlSearchParams)
this.getAll = urlSearchParams.getAll.bind(urlSearchParams)
this.has = urlSearchParams.has.bind(urlSearchParams)
this.keys = urlSearchParams.keys.bind(urlSearchParams)
this.values = urlSearchParams.values.bind(urlSearchParams)
this.toString = urlSearchParams.toString.bind(urlSearchParams)
}
[Symbol.iterator]() {
return this[INTERNAL_URLSEARCHPARAMS_INSTANCE][Symbol.iterator]()
}
append() {
throw readonlyURLSearchParamsError()
}
delete() {
throw readonlyURLSearchParamsError()
}
set() {
throw readonlyURLSearchParamsError()
}
sort() {
throw readonlyURLSearchParamsError()
}
}
/**
* Get the current search params. For example useSearchParams() would return {"foo": "bar"} when ?foo=bar
*/
export function useSearchParams() {
const searchParams = useContext(SearchParamsContext)
const readonlySearchParams = useMemo(() => {
return new ReadonlyURLSearchParams(searchParams)
}, [searchParams])
return readonlySearchParams
}
// TODO-APP: Move the other router context over to this one
/**
* Get the router methods. For example router.push('/dashboard')
*/
export function useRouter(): import('../../shared/lib/app-router-context').AppRouterInstance {
return useContext(AppRouterContext)
}
// TODO-APP: getting all params when client-side navigating is non-trivial as it does not have route matchers so this might have to be a server context instead.
// export function useParams() {
// return useContext(ParamsContext)
// }
/**
* Get the current pathname. For example usePathname() on /dashboard?foo=bar would return "/dashboard"
*/
export function usePathname(): string {
return useContext(PathnameContext)
}
// TODO-APP: define what should be provided through context.
// export function useLayoutSegments() {
// return useContext(LayoutSegmentsContext)
// }
// TODO-APP: Expand description when the docs are written for it.
/**
* Get the current segment one level down from the layout.
*/
export function useSelectedLayoutSegment(
parallelRouteKey: string = 'children'
): string {
const { tree } = useContext(LayoutRouterContext)
const segment = tree[1][parallelRouteKey][0]
return Array.isArray(segment) ? segment[1] : segment
}