createContext を Server Component で使えない
実際に出るエラー
Server Component のファイルで createContext を使ったときに出るビルドエラーです。
何が起きているか
'use client' の無いファイルは Server Component です。そこで createContext を import すると、ビルドがその行で止まります。
import { createContext } from 'react' // ← ここで止まる
const Ctx = createContext('light')
export default function Page() {
return <Ctx.Provider value="dark">...</Ctx.Provider>
}
Context は、ブラウザで動いている部品の間で値を配る仕組みです。Server Component はサーバーで一度描かれて終わるので、配る先がありません。
テーマやログイン中のユーザーを配ろうとして、ライブラリの Provider を layout に置いたときによく出ます。
直し方:Provider だけを切り出す
Context を作って配るところだけを、'use client' のファイルに移します。
// app/providers.tsx
'use client'
import { createContext, useContext } from 'react'
const Ctx = createContext('light')
export function Providers({ children }: { children: React.ReactNode }) {
return <Ctx.Provider value="dark">{children}</Ctx.Provider>
}
export function useTheme() {
return useContext(Ctx)
}
layout は Server Component のまま、Providers で children を包みます。
// app/layout.tsx(use client は書かない)
import { Providers } from './providers'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ja">
<body>
<Providers>{children}</Providers>
</body>
</html>
)
}
実機で確かめました。ビルドが通り、中の部品が useContext で読んだ値は「dark」でした。
包まれたページは Server Component のまま
Providers は Client Component ですが、包まれた側まで Client Component になるわけではありません。
- Serverページを描く
ページは Server Component のまま、サーバーで描かれる。
- Server描いた結果を children として渡す
Providers が受け取るのは出来上がった結果。ページのコードではない。
- BrowserProviders が値を配る
その下の Client Component が useContext で読める。
実機では、Providers で body の中を丸ごと包んでも、ページはビルド時に描かれたままでした。
読む側も Client Component
useContext もフックなので、Context の値を読む部品も 'use client' のファイルに置きます。
Server Component で使いたい値(ログイン中のユーザーなど)は、Context を通さずにサーバーで直接取得してください。Context はブラウザ側で配るためのものです。
同じ形のエラー
useState を Server Component で使ったときも、同じ形の文言が出ます。文言の中の関数名が違うだけです。直し方の考え方も同じで、使う部品だけを 'use client' のファイルに切り出します(useState を import できないエラー)。
出典
- nextjs.org/docs/app/getting-started/server-and-client-components
- react.dev/reference/react/createContext
このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。