/
adapters.tsx
136 lines (124 loc) · 3.95 KB
/
adapters.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import type { ParsedUrlQuery } from 'node:querystring'
import React, { useMemo, useRef } 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.
*
* @param router the NextRouter to adapt
* @returns an AppRouterInstance
*/
export function adaptForAppRouterInstance(
router: NextRouter
): AppRouterInstance {
return {
back(): void {
router.back()
},
forward(): void {
router.forward()
},
refresh(): void {
router.reload()
},
push(href: string): void {
void router.push(href)
},
replace(href: string): void {
void router.replace(href)
},
prefetch(href: string): void {
void router.prefetch(href)
},
}
}
/**
* transforms the ParsedUrlQuery into a URLSearchParams.
*
* @param query the query to transform
* @returns URLSearchParams
*/
function transformQuery(query: ParsedUrlQuery): URLSearchParams {
const params = new URLSearchParams()
for (const [name, value] of Object.entries(query)) {
if (Array.isArray(value)) {
for (const val of value) {
params.append(name, val)
}
} else if (typeof value !== 'undefined') {
params.append(name, value)
}
}
return params
}
/**
* adaptForSearchParams transforms the ParsedURLQuery into URLSearchParams.
*
* @param router the router that contains the query.
* @returns the search params in the URLSearchParams format
*/
export function adaptForSearchParams(
router: Pick<NextRouter, 'isReady' | 'query'>
): URLSearchParams {
if (!router.isReady || !router.query) {
return new URLSearchParams()
}
return transformQuery(router.query)
}
export function PathnameContextProviderAdapter({
children,
router,
...props
}: React.PropsWithChildren<{
router: Pick<NextRouter, 'pathname' | 'asPath' | 'isReady' | 'isFallback'>
isAutoExport: boolean
}>) {
const ref = useRef(props.isAutoExport)
const value = useMemo(() => {
// isAutoExport is only ever `true` on the first render from the server,
// so reset it to `false` after we read it for the first time as `true`. If
// we don't use the value, then we don't need it.
const isAutoExport = ref.current
if (isAutoExport) {
ref.current = false
}
// When the route is a dynamic route, we need to do more processing to
// determine if we need to stop showing the pathname.
if (isDynamicRoute(router.pathname)) {
// When the router is rendering the fallback page, it can't possibly know
// the path, so return `null` here. Read more about fallback pages over
// at:
// https://nextjs.org/docs/api-reference/data-fetching/get-static-paths#fallback-pages
if (router.isFallback) {
return null
}
// When `isAutoExport` is true, meaning this is a page page has been
// automatically statically optimized, and the router is not ready, then
// we can't know the pathname yet. Read more about automatic static
// optimization at:
// https://nextjs.org/docs/advanced-features/automatic-static-optimization
if (isAutoExport && !router.isReady) {
return null
}
}
// The `router.asPath` contains the pathname seen by the browser (including
// any query strings), so it should have that stripped. Read more about the
// `asPath` option over at:
// https://nextjs.org/docs/api-reference/next/router#router-object
let url: URL
try {
url = new URL(router.asPath, 'http://f')
} catch (_) {
// fallback to / for invalid asPath values e.g. //
return '/'
}
return url.pathname
}, [router.asPath, router.isFallback, router.isReady, router.pathname])
return (
<PathnameContext.Provider value={value}>
{children}
</PathnameContext.Provider>
)
}