fix(navigation): allow useSelectedLayoutSegment(s)
in Pages Router (#62584)
### What? Do not fail when `useSelectedLayoutSegment` or `useSelectedLayoutSegments` APIs are called in the Pages Router. ### Why? This makes migration easier and creates consistency with our other App Router-specific APIs that inherit the same behavior. ### How? Similar to #47490, we return `null` if there is no Layout context (indicating being in Pages Router) Types are also overridden in the navigation compact module declaration which kicks in during start to correct the types if we detect a `pages/` directory. Note to reviewer: #47490 didn't add a test, so I added one top-level, let me know if you have a better suggestion for placing. Closes NEXT-2506 Fixes #61464
This commit is contained in:
parent
c353b5f0e6
commit
9ae437f4b1
5 changed files with 49 additions and 4 deletions
|
@ -31,4 +31,20 @@ declare module 'next/navigation' {
|
|||
string | string[]
|
||||
>
|
||||
>(): T | null
|
||||
|
||||
/**
|
||||
* A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook
|
||||
* that lets you read the active route segments **below** the Layout it is called from.
|
||||
*
|
||||
* If used from `pages/`, the hook will return `null`.
|
||||
*/
|
||||
export function useSelectedLayoutSegments(): string[] | null
|
||||
|
||||
/**
|
||||
* A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook
|
||||
* that lets you read the active route segment **one level below** the Layout it is called from.
|
||||
*
|
||||
* If used from `pages/`, the hook will return `null`.
|
||||
*/
|
||||
export function useSelectedLayoutSegment(): string | null
|
||||
}
|
||||
|
|
|
@ -211,8 +211,11 @@ function useSelectedLayoutSegments(
|
|||
parallelRouteKey: string = 'children'
|
||||
): string[] {
|
||||
clientHookInServerComponentError('useSelectedLayoutSegments')
|
||||
const { tree } = useContext(LayoutRouterContext)
|
||||
return getSelectedLayoutSegmentPath(tree, parallelRouteKey)
|
||||
const context = useContext(LayoutRouterContext)
|
||||
// @ts-expect-error This only happens in `pages`. Type is overwritten in navigation.d.ts
|
||||
if (!context) return null
|
||||
|
||||
return getSelectedLayoutSegmentPath(context.tree, parallelRouteKey)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -238,7 +241,8 @@ function useSelectedLayoutSegment(
|
|||
): string | null {
|
||||
clientHookInServerComponentError('useSelectedLayoutSegment')
|
||||
const selectedLayoutSegments = useSelectedLayoutSegments(parallelRouteKey)
|
||||
if (selectedLayoutSegments.length === 0) {
|
||||
|
||||
if (!selectedLayoutSegments || selectedLayoutSegments.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
|
@ -148,7 +148,8 @@ export const LayoutRouterContext = React.createContext<{
|
|||
childNodes: CacheNode['parallelRoutes']
|
||||
tree: FlightRouterState
|
||||
url: string
|
||||
}>(null as any)
|
||||
} | null>(null)
|
||||
|
||||
export const GlobalLayoutRouterContext = React.createContext<{
|
||||
buildId: string
|
||||
tree: FlightRouterState
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import {
|
||||
useSelectedLayoutSegment,
|
||||
useSelectedLayoutSegments,
|
||||
} from 'next/navigation'
|
||||
|
||||
export default function Page() {
|
||||
useSelectedLayoutSegment()
|
||||
useSelectedLayoutSegments()
|
||||
return <p id="hello-world">Hello World</p>
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import { nextTestSetup } from 'e2e-utils'
|
||||
|
||||
describe('useSelectedLayoutSegment(s) in Pages Router', () => {
|
||||
const { next } = nextTestSetup({ files: __dirname })
|
||||
|
||||
it('Should render with `useSelectedLayoutSegment(s) hooks', async () => {
|
||||
const browser = await next.browser('/')
|
||||
|
||||
await browser.waitForElementByCss('#hello-world')
|
||||
expect(await browser.elementByCss('#hello-world').text()).toBe(
|
||||
'Hello World'
|
||||
)
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue