Skip to content

Commit

Permalink
feat: change usePathname to return string | null
Browse files Browse the repository at this point in the history
  • Loading branch information
wyattjoh committed Nov 2, 2022
1 parent 9de871e commit e114bd1
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 34 deletions.
10 changes: 2 additions & 8 deletions packages/next/client/components/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ export function useSearchParams() {
throw new Error('invariant expected search params to be mounted')
}

// eslint-disable-next-line react-hooks/rules-of-hooks
const readonlySearchParams = useMemo(() => {
return new ReadonlyURLSearchParams(searchParams)
}, [searchParams])
Expand All @@ -87,14 +86,9 @@ export function useSearchParams() {
/**
* Get the current pathname. For example usePathname() on /dashboard?foo=bar would return "/dashboard"
*/
export function usePathname(): string {
export function usePathname(): string | null {
staticGenerationBailout('usePathname')
const pathname = useContext(PathnameContext)
if (pathname === null) {
throw new Error('invariant expected pathname to be mounted')
}

return pathname
return useContext(PathnameContext)
}

// 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.
Expand Down
15 changes: 8 additions & 7 deletions packages/next/client/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,10 @@ import { hasBasePath } from './has-base-path'
import { AppRouterContext } from '../shared/lib/app-router-context'
import {
adaptForAppRouterInstance,
adaptForPathname,
adaptForSearchParams,
PathnameContextProviderAdapter,
} from '../shared/lib/router/adapters'
import {
PathnameContext,
SearchParamsContext,
} from '../shared/lib/hooks-client-context'
import { SearchParamsContext } from '../shared/lib/hooks-client-context'

/// <reference types="react-dom/experimental" />

Expand Down Expand Up @@ -316,7 +313,11 @@ function AppContainer({
>
<AppRouterContext.Provider value={adaptForAppRouterInstance(router)}>
<SearchParamsContext.Provider value={adaptForSearchParams(router)}>
<PathnameContext.Provider value={adaptForPathname(asPath)}>
<PathnameContextProviderAdapter
router={router}
isAutoExport={self.__NEXT_DATA__.autoExport ?? false}
isFallback={initialData.isFallback ?? false}
>
<RouterContext.Provider value={makePublicRouterInstance(router)}>
<HeadManagerContext.Provider value={headManager}>
<ImageConfigContext.Provider
Expand All @@ -328,7 +329,7 @@ function AppContainer({
</ImageConfigContext.Provider>
</HeadManagerContext.Provider>
</RouterContext.Provider>
</PathnameContext.Provider>
</PathnameContextProviderAdapter>
</SearchParamsContext.Provider>
</AppRouterContext.Provider>
</Container>
Expand Down
15 changes: 8 additions & 7 deletions packages/next/server/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,11 @@ import stripAnsi from 'next/dist/compiled/strip-ansi'
import { stripInternalQueries } from './internal-utils'
import {
adaptForAppRouterInstance,
adaptForPathname,
adaptForSearchParams,
PathnameContextProviderAdapter,
} from '../shared/lib/router/adapters'
import { AppRouterContext } from '../shared/lib/app-router-context'
import {
PathnameContext,
SearchParamsContext,
} from '../shared/lib/hooks-client-context'
import { SearchParamsContext } from '../shared/lib/hooks-client-context'

let tryGetPreviewData: typeof import('./api-utils/node').tryGetPreviewData
let warn: typeof import('../build/output/log').warn
Expand Down Expand Up @@ -621,7 +618,11 @@ export async function renderToHTML(
const AppContainer = ({ children }: { children: JSX.Element }) => (
<AppRouterContext.Provider value={appRouter}>
<SearchParamsContext.Provider value={adaptForSearchParams(router)}>
<PathnameContext.Provider value={adaptForPathname(asPath)}>
<PathnameContextProviderAdapter
router={router}
isAutoExport={isAutoExport}
isFallback={isFallback}
>
<RouterContext.Provider value={router}>
<AmpStateContext.Provider value={ampState}>
<HeadManagerContext.Provider
Expand All @@ -648,7 +649,7 @@ export async function renderToHTML(
</HeadManagerContext.Provider>
</AmpStateContext.Provider>
</RouterContext.Provider>
</PathnameContext.Provider>
</PathnameContextProviderAdapter>
</SearchParamsContext.Provider>
</AppRouterContext.Provider>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { ParsedUrlQuery } from 'node:querystring'
import { AppRouterInstance } from '../app-router-context'
import { NextRouter } from './router'
import React, { useMemo } from 'react'
import type { AppRouterInstance } from '../app-router-context'
import { PathnameContext } from '../hooks-client-context'
import type { NextRouter } from './router'
import { isDynamicRoute } from './utils'

/**
* adaptForAppRouterInstance implements the AppRouterInstance with a NextRouter.
Expand Down Expand Up @@ -61,21 +64,42 @@ function transformQuery(query: ParsedUrlQuery): URLSearchParams {
* @param router the router that contains the query.
* @returns the search params in the URLSearchParams format
*/
export function adaptForSearchParams(router: NextRouter): URLSearchParams {
export function adaptForSearchParams(
router: Pick<NextRouter, 'isReady' | 'query'>
): URLSearchParams {
if (!router.isReady || !router.query) {
return new URLSearchParams()
}

return transformQuery(router.query)
}

/**
* adaptForPathname adapts the `asPath` parameter from the router to a pathname.
*
* @param asPath the asPath parameter to transform that comes from the router
* @returns pathname part of `asPath`
*/
export function adaptForPathname(asPath: string): string {
const url = new URL(asPath, 'http://f')
return url.pathname
export function PathnameContextProviderAdapter({
children,
router,
isAutoExport,
isFallback,
}: React.PropsWithChildren<{
router: Pick<NextRouter, 'pathname' | 'asPath'>
isAutoExport: boolean
isFallback: boolean
}>) {
const value = useMemo(() => {
// If this is a dynamic route with auto export or fallback is true...
if (isDynamicRoute(router.pathname) && (isAutoExport || isFallback)) {
// Return null. This will throw an error when accessed via `usePathname`,
// but it provides the correct API for folks considering the new router
// does not support `isReady`.
return null
}

const url = new URL(router.asPath, 'http://f')
return url.pathname
}, [router.pathname, router.asPath, isAutoExport, isFallback])

return (
<PathnameContext.Provider value={value}>
{children}
</PathnameContext.Provider>
)
}

0 comments on commit e114bd1

Please sign in to comment.