Next.jsのAppRouterでlayout.tsxからServer/Clientコンポーネントにデータを配る
Contextを利用できないServerコンポーネント🔗
Next.jsのAppRouterのServerコンポーネントでは、createContextが使用できません。これに加え、Next.jsではcreateServerContextも使用できなくなったため、Contextを使ってデータをコンポーネントツリーに配ることができません。
PagesRouter上では_app.tsxからContextを配ることが出来てとても便利でした。しかしAppRouterのServerコンポーネントにはその機能がありません。
そもそもpage.tsxがlayout.tsxよりも先に実行される🔗
AppRouterのコンポーネント評価順序は以下のようになります。
(1) page.tsx
(2) layout.tsx
つまり、先にpage.tsxが実行され、その後layout.tsxのコンポーネントが評価されます。そのため、layoutからpageにデータを配ろうにも、データを配る前にpageが実行されてしまい、データを配ることができません。
データを配ることも出来なければ、そもそも実行順序も違う🔗
そう、不可能なのです。普通にやったら。
不可能を可能にする🔗
ということで、不可能を可能とするコードを書きました。
npmにパッケージ化したものを登録してあります。
https://www.npmjs.com/package/next-approuter-context
サンプルソース🔗
- GitHub
https://github.com/SoraKumo001/next-approuter-context-test - Vercel
https://next-approuter-context-test.vercel.app/ - CodeSandbox
https://codesandbox.io/p/github/SoraKumo001/next-approuter-context-test/master
app/context.ts🔗
Serverコンポーネント間で共有するコンテキストを生成します。
Server/Client間でContextのインスタンスを自動識別する方法がどうしても思いつかなかったので、複数のContextを扱うときはユニークな名前が必要です。
1import { createMixContext } from "next-approuter-context";23export const context1 = createMixContext<{ text: string; color: string }>(4 "context1"5);6export const context2 = createMixContext<number>("context2");
app/layout.tsx🔗
既存のContextAPIに似せてProviderを作る書き方にしています。
1import { context1, context2 } from "./context";23export default function RootLayout({4 children,5}: {6 children: React.ReactNode;7}) {8 return (9 <html lang="en">10 <body>11 <context1.Provider12 value={{ text: "Send colors and text from Layout", color: "red" }}13 >14 <context2.Provider value={123456}>{children}</context2.Provider>15 </context1.Provider>16 </body>17 </html>18 );19}
app/page.tsx🔗
動作確認用にServer/Clientコンポーネントを呼び出します。
1import { Client } from "./client";2import { Server } from "./server";34const Page = () => {5 return (6 <>7 <Server />8 <Client />9 </>10 );11};1213export default Page;
app/server.tsx🔗
ServerコンポーネントからContextを取得しています。コンポーネントをasyncにする場合は、データの取得方法がgetMixContextを使った形に書き換える必要があります。
1"use server";23import { useMixContext } from "next-approuter-context";4import { context1, context2 } from "./context";56export const Server = () => {7 // If the component is async, it should be written as follows8 // const { text, color } = await getMixContext<ContextType1>();9 const { text, color } = useMixContext(context1);10 const value = useMixContext(context2);11 return (12 <>13 <div style={{ color }}>14 Server: {text} - {value}15 </div>16 </>17 );18};
app/client.tsx🔗
ClientコンポーネントからContextを取得しています。基本的にServerコンポーネントと同じコードになるように、ライブラリを作ってあります。
1"use client";23import { useMixContext } from "next-approuter-context";4import { context1, context2 } from "./context";56export const Client = () => {7 const { text, color } = useMixContext(context1);8 const value = useMixContext(context2);9 return (10 <>11 <div style={{ color }}>12 Client: {text} - {value}13 </div>14 </>15 );16};
実行結果🔗
見事、データが配れない問題と実行順序の問題を解決しました。
色々なことを力技で解決していますが、具体的なソースはこちらを見てください。
https://github.com/ReactLibraries/next-approuter-context/tree/master/src
まとめ🔗
Server/Client の両方に layout.tsx からデータを配れるようになりました。このやり方を使えば、UI ライブラリのテーマ設定も簡単です。