Open4
RSC/App Routerで発生するpropsのバケツリレー
Next.jsではdynamic routingのパラメータやSearchParamsを受け取る方法がmiddleware/サーバーコンポーネント(RSC)/クライアントコンポーネント(RCC)で異なる
middleware
export default function middleware(req: NextRequest) {
const {pathname, searchParams } = req.nextUrl
}
RSC(ページのみ)
// /foo/[locale]?q=hoge へのアクセス
export default Page({ params: { locale }, searchParams: { q } }: Props) {
...
}
RCC
// /components/breadcrumbs
export default Breadcrumbs() {
const params = useParams()
const searchParams = useSearchParams()
}
ページでないRSCでは現状route paramsやSearchParamsを直接取得する方法がなく,バケツリレーをするしか有効な手段がない
props-drilling
export default function ParentPage({ params: { locale } }: Props) {
return <Child1 locale={locale} />
}
export function Child1({ locale }: Props) {
// This component does not use locale
return <Child2 locale={locale} />
}
export function Child2({ locale }: Props) {
return <span>{translation[locale].foo}</span>
}
検討案としてはReactのcacheを用いることでスコープ外から直接取り出す
import 'server-only';
import { cache } from 'react';
const requestContext = cache(() => {
return new Map<string, string>();
});
export const setRequestContext = (key: string, value: string) =>
requestContext().set(key, value);
export const getRequestContext = (key: string) => requestContext().get(key);
server-only-context
なんていうパッケージもある.比較的簡単に実装できる
ただこの場合RSCをRCCの中で呼ばないという固い制約を結ばなければいけなくなり再利用性がなくなる可能性も
export function RSC() {
const params = getRequestContext('params')
return <SomeComponent />
}
'use client'
export function RCC() {
return (
<div>
<RSC /> {/* これはエラー */}
</div>
)
}
Reactのcache
は今のところexperimentalなのでNext.jsでは代わりにheaders
を使えば破壊的変更には耐えられるかも
デフォルトでheaderからurlを取得する方法はないのでmiddlewareでセットする
export default function middleware(req: NextRequest) {
const requestHeaders = new Headers(request.headers)
requestHeaders.set('X-Pathname', request.nextUrl.pathname)
requestHeaders.set('X-SearchParams', request.nextUrl.searchParams.toString())
return NextResponse.next({
request: {
headers: requestHeaders
}
})
}
import 'server-only'
import { headers } from 'next/headers'
export function getParams() {
const pathname = headers().get('X-Pathname')
return pathname ? pathnameToParams(pathname) : {}
}
export function getSearchParams() {
const searchParams = headers().get('x-SearchParams')
return searchParams ? new URLSearchParams(searchParams) : new URLSearchParams()
}