Next.js App Router の末端にある Client Components で懐かしの C/P パターン
Posted: Updated:
C/P = Container/Presentational
タイトルに入れ込むには長かったので...
- Next.js App Router を使用
- Client Component の境界を越えた先はクライアントサイドバンドルに載ってしまう
- よってツリーの末端に移動させるのが最適化の基本方針になる
- Client Component になる理由は DOM 操作が必要なパターン e.g.
<Carousel>
- もしくはクライアントサイドでのデータ取得が必要なパターン e.g.
useQuery()
- 内部でデータフェッチすると test や storybook でのモックがちと面倒に感じる
サンプルコード
じゃあ局所的な Container/Presentational でいいんじゃね、ということでこんな感じ(大分極端ですがイメージです)
export const ClientSideFetchedList = () => {
const { data } = useQuery(...);
return (
<__ClientSideFetchedList items={data?.list || []} />
);
};
export const __ClientSideFetchedList = ({ items }) => {
return (
<ul>
{items.map((item, i) => <li key={i}>{item.name}</li>)};
</ul>
);
};
Test や Storybook では <__ClientSideFetchedList>
だけを import して取り扱います。
Server Components でも同様のパターンが有効と思われますが、手元では Server Components らしい働きをするのは上位層のコンポーネントのみに留める古典の広域的 Container/Presentational パターンに寄せています。
server の根元と client の末端に寄せていくスタイル
総じて Server Components の根元と、Client Components の末端に処理が寄っていきました。中間のコンポーネントは普通の React Component として取り回せるので現状の App Router 環境の成熟度を鑑みるとアリな気はします。
App Router はともかくとしても storybook や test は不便すぎない程度にシンプルな形を保っておいたほうが、エコシステムのアップデート耐性も上がるのではないかと思う次第。ソフトウェアのメンテナンスコストにおいてサプライチェーンのアップデートが占める割合が高くなりやすい Web アプリケーション界隈はそういうアプローチもありなんじゃないかなと。
勇気
世間的な自信に乏しかったのですが Container/Presentational の適用が下記のリソースでも触れられていたので、勇気を出して手元での例も書いてみました :)
- React Server Component のテストと Container / Presentation Separation | by Yosuke Kurami | Medium
- Next.js App Router での MPA フロントエンド刷新 - Speaker Deck