空雲 Blog

Eye catchNext.jsのAppRouterでlayout.tsxを先に実行しpage.tsxにデータを配る(14系統版)

publication: 2024/03/09
update:2024/03/10

実行順序

昨今、AppRouter で page.tsx が layout.tsx より先に実行されることが話題になっていますが、去年こんな記事を書きました。

https://next-blog.croud.jp/contents/1fec8d34-dc69-4a64-9128-c7db2a596f72

箸にも棒にも引っかからなかったこの記事ですが、次元を歪めることに成功し実行順序を変えています。しかし久々に実行してみると Next.js の仕様が変わって、"use server"したファイルが非同期関数しかエクスポートできないという仕様に変わっており、修正が必要になっていたので、焼き直しで記事を書くことにしました。

layout.tsx を page.tsx より先に実行する

ということでやってみます。

Sample コードと実行環境はこちら

app/context.tsx

ReactのContextAPI風にコンテキストを作ります。

import { createMixContext } from "next-approuter-context"; export const context1 = createMixContext<{ text: string; color: string }>( "context1" ); export const context2 = createMixContext<number>("context2");

app/layout.tsx

レイアウトにデータを設置します。当然のごとく、page.tsxよりもlayout.tsxが先に実行が完了されないとデータを配れません。

import { context1, context2 } from "./context"; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> <body> <context1.Provider value={{ text: "Send colors and text from Layout", color: "red" }} > <context2.Provider value={123456}>{children}</context2.Provider> </context1.Provider> </body> </html> ); }

app/page.tsx

Server/Clientの両方のコンポーネントを呼び出します。

import { Client } from "./client"; import { Server } from "./server"; const Page = () => { return ( <> <Server /> <Client /> </> ); }; export default Page;

app/server.tsx

Server component で、layout.tsxからのデータを受け取ります。

"use server"; import { context1, context2 } from "./context"; import { getMixContext } from "next-approuter-context"; export const Server = async () => { const { text, color } = await getMixContext(context1); const value = await getMixContext(context2); return ( <> <div style={{ color }}> Server: {text} - {value} </div> </> ); };

app/client.tsx

Client component で、layout.tsxからのデータを受け取ります。

"use client"; import { useMixContext } from "next-approuter-context"; import { context1, context2 } from "./context"; export const Client = () => { const { text, color } = useMixContext(context1); const value = useMixContext(context2); return ( <> <div style={{ color }}> Client: {text} - {value} </div> </> ); };

実行結果

{"width":"525px","height":"66px"}

まとめ

AppRouter で layout.tsx の実行を先に完了させ、さらに page.tsx へデータを配ることに成功しました。React の renderToReadableStream を使っている Next.js や Remix の場合、コンポーネントの処理中に保留と再開が任意に可能です。つまりサーバ側のレンダリング処理でそのタイミングを制御するのは、やろうと思えばいくらでも可能なのです。今回も、単にデータ配布の非同期解決まで page.tsx を待たせているだけで、大したことはしていません。

まずはそれが可能だと思うことが重要です。