🌝
Nextのapp routerでuseSearchParamsを使うとページ全体がクライアントに読み込まれてしまう
経緯
headerのコンポーネント内ででuseSearchParamsを使ったらEntire page deopted into client-side rendering
とエラーが出てしまいました。
公式のドキュメントを見てみます。
During static rendering, the entire page was deopted into client-side rendering by useSearchParams as there was no Suspense boundary that caught it.
If a route is statically rendered, calling useSearchParams() will cause the tree up to the closest Suspense boundary to be client-side rendered.
This allows a part of the page to be statically rendered while the dynamic part that uses searchParams can be client-side rendered.
要は、layout.tsxに配置したコンポーネントでuseSearchParamsを使うとサスペンス境界に当たるまで遡ってしまって結果的にページ全体がクライアント側に読み込まれてしまう、ということらしい。
対処法のコードを見る
app/dashboard/search-bar.tsx
'use client'
import { useSearchParams } from 'next/navigation'
export default function SearchBar() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
// This will not be logged on the server when using static rendering
console.log(search)
return <>Search: {search}</>
}
app/dashboard/page.tsx
import { Suspense } from 'react'
import SearchBar from './search-bar'
// This component passed as fallback to the Suspense boundary
// will be rendered in place of the search bar in the initial HTML.
// When the value is available during React hydration the fallback
// will be replaced with the `<SearchBar>` component.
function SearchBarFallback() {
return <>placeholder</>
}
export default function Page() {
return (
<>
<nav>
<Suspense fallback={<SearchBarFallback />}>
<SearchBar />
</Suspense>
</nav>
<h1>Dashboard</h1>
</>
)
}
これでエラー解消
return (
<Suspense>
<Header/>
</Suspense>
)
fallbackとか使っていて複雑に見えますが、別に使わなくても大丈夫で<suspense>
で囲うだけでエラーが解消しました。
Discussion