Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move redirect and notFound to navigation exports #41703

Merged
merged 1 commit into from Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,22 +1,12 @@
// useLayoutSegments() // Only the segments for the current place. ['children', 'dashboard', 'children', 'integrations'] -> /dashboard/integrations (/dashboard/layout.js would get ['children', 'dashboard', 'children', 'integrations'])
'use client'

import type { FlightRouterState } from '../../server/app-render'
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'
} from '../hooks-client-context'

const INTERNAL_URLSEARCHPARAMS_INSTANCE = Symbol(
'internal for urlsearchparams readonly'
Expand Down Expand Up @@ -81,85 +71,24 @@ export function useSearchParams() {
return readonlySearchParams
}

// TODO-APP: Move the other router context over to this one
/**
* Get the router methods. For example router.push('/dashboard')
* Get the current pathname. For example usePathname() on /dashboard?foo=bar would return "/dashboard"
*/
export function useRouter(): import('../../shared/lib/app-router-context').AppRouterInstance {
return useContext(AppRouterContext)
export function usePathname(): string {
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.
// 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: handle parallel routes
function getSelectedLayoutSegmentPath(
tree: FlightRouterState,
parallelRouteKey: string,
first = true,
segmentPath: string[] = []
): string[] {
let node: FlightRouterState
if (first) {
// Use the provided parallel route key on the first parallel route
node = tree[1][parallelRouteKey]
} else {
// After first parallel route prefer children, if there's no children pick the first parallel route.
const parallelRoutes = tree[1]
node = parallelRoutes.children ?? Object.values(parallelRoutes)[0]
}

if (!node) return segmentPath
const segment = node[0]
const segmentValue = Array.isArray(segment) ? segment[1] : segment
if (!segmentValue) return segmentPath

segmentPath.push(segmentValue)

return getSelectedLayoutSegmentPath(
node,
parallelRouteKey,
false,
segmentPath
)
}

// TODO-APP: Expand description when the docs are written for it.
/**
* Get the canonical segment path from the current level to the leaf node.
*/
export function useSelectedLayoutSegments(
parallelRouteKey: string = 'children'
): string[] {
const { tree } = useContext(LayoutRouterContext)
return getSelectedLayoutSegmentPath(tree, parallelRouteKey)
}

// TODO-APP: Expand description when the docs are written for it.
/**
* Get the segment below the current level
*/
export function useSelectedLayoutSegment(
parallelRouteKey: string = 'children'
): string {
const selectedLayoutSegments = useSelectedLayoutSegments(parallelRouteKey)
if (selectedLayoutSegments.length === 0) {
throw new Error('No selected layout segment below the current level')
}

return selectedLayoutSegments[0]
}
export {
ServerInsertedHTMLContext,
useServerInsertedHTML,
} from '../../../shared/lib/server-inserted-html'
85 changes: 85 additions & 0 deletions packages/next/client/components/navigation/index.ts
@@ -0,0 +1,85 @@
// useLayoutSegments() // Only the segments for the current place. ['children', 'dashboard', 'children', 'integrations'] -> /dashboard/integrations (/dashboard/layout.js would get ['children', 'dashboard', 'children', 'integrations'])

import type { FlightRouterState } from '../../../server/app-render'
import { useContext } from 'react'

import {
AppRouterContext,
LayoutRouterContext,
} from '../../../shared/lib/app-router-context'

// 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: handle parallel routes
function getSelectedLayoutSegmentPath(
tree: FlightRouterState,
parallelRouteKey: string,
first = true,
segmentPath: string[] = []
): string[] {
let node: FlightRouterState
if (first) {
// Use the provided parallel route key on the first parallel route
node = tree[1][parallelRouteKey]
} else {
// After first parallel route prefer children, if there's no children pick the first parallel route.
const parallelRoutes = tree[1]
node = parallelRoutes.children ?? Object.values(parallelRoutes)[0]
}

if (!node) return segmentPath
const segment = node[0]
const segmentValue = Array.isArray(segment) ? segment[1] : segment
if (!segmentValue) return segmentPath

segmentPath.push(segmentValue)

return getSelectedLayoutSegmentPath(
node,
parallelRouteKey,
false,
segmentPath
)
}

// TODO-APP: Expand description when the docs are written for it.
/**
* Get the canonical segment path from the current level to the leaf node.
*/
export function useSelectedLayoutSegments(
parallelRouteKey: string = 'children'
): string[] {
const { tree } = useContext(LayoutRouterContext)
return getSelectedLayoutSegmentPath(tree, parallelRouteKey)
}

// TODO-APP: Expand description when the docs are written for it.
/**
* Get the segment below the current level
*/
export function useSelectedLayoutSegment(
parallelRouteKey: string = 'children'
): string {
const selectedLayoutSegments = useSelectedLayoutSegments(parallelRouteKey)
if (selectedLayoutSegments.length === 0) {
throw new Error('No selected layout segment below the current level')
}

return selectedLayoutSegments[0]
}

export { redirect } from '../redirect'
export { notFound } from '../not-found'

export {
useSearchParams,
usePathname,
ServerInsertedHTMLContext,
useServerInsertedHTML,
} from './client'
2 changes: 1 addition & 1 deletion test/e2e/app-dir/app/app/not-found/client-side/page.js
@@ -1,6 +1,6 @@
'use client'

import { notFound } from 'next/dist/client/components/not-found'
import { notFound } from 'next/navigation'
import React from 'react'

export default function Page() {
Expand Down
@@ -1,5 +1,5 @@
'use client'
import { notFound } from 'next/dist/client/components/not-found'
import { notFound } from 'next/navigation'

export default function ClientComp() {
notFound()
Expand Down
@@ -1,5 +1,5 @@
// TODO-APP: enable when flight error serialization is implemented
import { notFound } from 'next/dist/client/components/not-found'
import { notFound } from 'next/navigation'

export default function Page() {
notFound()
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/app-dir/app/app/redirect/client-side/page.js
@@ -1,6 +1,6 @@
'use client'

import { redirect } from 'next/dist/client/components/redirect'
import { redirect } from 'next/navigation'
import React from 'react'

export default function Page() {
Expand Down
@@ -1,5 +1,5 @@
'use client'
import { redirect } from 'next/dist/client/components/redirect'
import { redirect } from 'next/navigation'

export default function ClientComp() {
redirect('/redirect/result')
Expand Down
@@ -1,4 +1,4 @@
import { redirect } from 'next/dist/client/components/redirect'
import { redirect } from 'next/navigation'

export default function Page() {
redirect('/redirect/result')
Expand Down