空雲 Blog

Eye catch
Next.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風にコンテキストを作ります。


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


app/layout.tsx🔗


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


1import { context1, context2 } from "./context";
2
3export default function RootLayout({
4 children,
5}: {
6 children: React.ReactNode;
7}) {
8 return (
9 <html lang="en">
10 <body>
11 <context1.Provider
12 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";
3
4const Page = () => {
5 return (
6 <>
7 <Server />
8 <Client />
9 </>
10 );
11};
12
13export default Page;


app/server.tsx🔗


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


1"use server";
2
3import { context1, context2 } from "./context";
4import { getMixContext } from "next-approuter-context";
5
6export const Server = async () => {
7 const { text, color } = await getMixContext(context1);
8 const value = await getMixContext(context2);
9 return (
10 <>
11 <div style={{ color }}>
12 Server: {text} - {value}
13 </div>
14 </>
15 );
16};


app/client.tsx🔗


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


1"use client";
2
3import { useMixContext } from "next-approuter-context";
4import { context1, context2 } from "./context";
5
6export 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};


実行結果🔗


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


まとめ🔗


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


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