mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
fixes NEXT-2173 Fixes #59968 ### TODOs - [x] recreate [repro](https://github.com/williamli/nextjs-NEXT-2173) - [x] patch `useSelectedLayoutSegment` to support parallel routes (see "What") - [x] check `useSelectedLayoutSegments` to see if it is affected - [x] add test cases - [x] finalise PR description ### What? `useSelectedLayoutSegment` does not return the name of the active state of parallel route slots. #### Expected Behaviour According to https://nextjs.org/docs/app/building-your-application/routing/parallel-routes#useselectedlayoutsegments > When a user navigates to app/@auth/login (or /login in the URL bar), loginSegments will be equal to the string "login". 👉🏽 We should update the docs to explain `null` and __DEFAULT__ result as well. According to the [API reference for useSelectedLayoutSegment](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segment#returns): > useSelectedLayoutSegment returns a string of the active segment or null if one doesn't exist. > For example, given the Layouts and URLs below, the returned segment would be: > <img width="881" alt="CleanShot 2024-01-20 at 14 50 52@2x" src="https://github.com/vercel/next.js/assets/179761/bfaa34c8-3139-4ec3-bd70-4346c682e36b"> #### Current Behaviour Currently a string "children" is returned for everything inside a parallel route with active state and `__DEFAULT__` is returned if there is no active state for the parallel route (since the `default.tsx` is loaded). ~`null` is returned when the `default.tsx` is not loaded (possibly caused by another bug, see test case 5).~ #### Reproduction [GitHub Repo](https://github.com/williamli/nextjs-NEXT-2173) is created based on the example provided in [Next.js docs for using `useSelectedLayoutSegment` with Parallel Routes](https://nextjs.org/docs/app/building-your-application/routing/parallel-routes#useselectedlayoutsegments). #### Test Cases 1. If you visit https://next-2173.vercel.app/, you get loginSegments: __DEFAULT__ (hard navigation) or children (soft navigation after returning from a visit to /login) 2. If you soft nav to (/app/@auth/login and /app/@nav/login) https://next-2173.vercel.app/login, you get 1. loginSegment: `children` (expected value should be `login`) 2. navSegment: `children` (expected value should be `login`) 3. If you soft nav to (/app/@auth/reset) https://next-2173.vercel.app/reset, you get 1. loginSegments: `children` (expected value should be `reset`) 2. navSegment: `children` (expected value should be `login`) 4. If you soft nav to (/app/@auth/reset/withEmail) https://next-2173.vercel.app/reset/withEmail, you get 1. loginSegments: `children` (expected value should be `withEmail`) 2. navSegment: `children` (expected value should be `login`) 5. ~If you hard nav to (/app/@auth/reset/withEmail) https://next-2173.vercel.app/reset/withEmail, you get an unexpected result due to possibly another bug:~ * ~navSegment is `null` on the deployed (Vercel) version, the navSlot is *not* loaded~ * ~navSegment is `__DEFAULT__` on local dev, the navSlot loads `/app/@nav/default.tsx`.~ ### Why? In `packages/next/src/client/components/navigation.ts`, `getSelectedLayoutSegmentPath` is called and returns the correct segmentPath for parallel routes (even though there is a TODO comment indicating this function needs to be updated to handle parallel routes) but `useSelectedLayoutSegment` failed to return the correct segment when a parallelRouteKey is provided. ### How? `useSelectedLayoutSegment` is updated to return selectedLayoutSegments[0] for non parallel routes (original logic), but it will return the last segments for parallel routes (or null if nothing is active). ``` return parallelRouteKey === 'children' ? selectedLayoutSegments[0] : selectedLayoutSegments[selectedLayoutSegments.length-1] ?? null ``` --------- Co-authored-by: Zack Tanner <[email protected]>
57 lines
1.6 KiB
TypeScript
57 lines
1.6 KiB
TypeScript
'use client'
|
|
|
|
import { useSelectedLayoutSegment } from 'next/navigation'
|
|
import Link from 'next/link'
|
|
|
|
export default function RootLayout({
|
|
children,
|
|
auth,
|
|
nav,
|
|
}: Readonly<{
|
|
children: React.ReactNode
|
|
auth: React.ReactNode
|
|
nav: React.ReactNode
|
|
}>) {
|
|
const authSegment = useSelectedLayoutSegment('auth')
|
|
const navSegment = useSelectedLayoutSegment('nav')
|
|
const routeSegment = useSelectedLayoutSegment()
|
|
|
|
return (
|
|
<html lang="en">
|
|
<body>
|
|
<section>
|
|
<nav>
|
|
<Link href="/">Main</Link>
|
|
<Link href="/foo">Foo (regular page)</Link>
|
|
<Link href="/login">
|
|
Login (/app/@auth/login) and (/app/@nav/login)
|
|
</Link>
|
|
<Link href="/reset">Reset (/app/@auth/reset)</Link>
|
|
<Link href="/reset/withEmail">
|
|
Reset with Email (/app/@auth/reset/withEmail)
|
|
</Link>
|
|
<Link href="/reset/withMobile">
|
|
Reset with Mobile (/app/@auth/reset/withMobile)
|
|
</Link>
|
|
</nav>
|
|
<div>
|
|
navSegment (parallel route): <div id="navSegment">{navSegment}</div>
|
|
</div>
|
|
<div>
|
|
authSegment (parallel route):{' '}
|
|
<div id="authSegment">{authSegment}</div>
|
|
</div>
|
|
<div>
|
|
routeSegment (app route):{' '}
|
|
<div id="routeSegment">{routeSegment}</div>
|
|
</div>
|
|
|
|
<section id="navSlot">{nav}</section>
|
|
<section id="authSlot">{auth}</section>
|
|
<section id="children">{children}</section>
|
|
</section>
|
|
</body>
|
|
</html>
|
|
)
|
|
}
|