サーバーコンポーネントのレンダリング
Reactの伝統的なコンポーネントであるクライアントコンポーネントは、自身の**状態(state)**または親の状態が変化するとリレンダリングが発生します。しかし、サーバーコンポーネントではフックを使用できません。つまり、サーバーコンポーネントは状態を持たないということです。以下の例のように、親の状態が変わってもサーバーコンポーネントはリレンダリングされません。では、サーバーコンポーネントはいつリレンダリングされるのでしょうか?

サーバーコンポーネントのレンダリング戦略
Next.jsの公式ドキュメントによると、Static、Dynamic、Streamingの3つの戦略でレンダリングが行われます。
Static Rendering
Static RenderingはNext.jsのデフォルトのレンダリング戦略です。この戦略の主な特徴は、ビルド時にコンポーネントを一度レンダリングし、その結果を再利用するという点です。
- ビルド時レンダリング
- サーバーコンポーネントはアプリケーションのビルド時にレンダリングされます
- レンダリング結果はCDNにキャッシュされ、すべてのユーザーに提供されます
 
- データの不変性
- 一度レンダリングされたコンポーネントのデータは基本的に変更されません
- ページを更新しても同じデータが表示されます
- クライアントの状態変更はサーバーコンポーネントのデータに影響を与えません
 
- Revalidationメカニズム
- revalidateTag()または- revalidatePath()を通じて手動でリレンダリングが可能です
 
- 
画面を更新した場合  stateを変更してリロードしても、データが変わらないことがわかります。 
- 
revalidateを行った場合  Next.jsの revalidateTagを実行すると、サーバーコンポーネントのキャッシュを無効化し、データを再取得してリレンダリングすることがわかります。
Dynamic Rendering
Dynamic Renderingは、各ユーザーリクエストごとにサーバーで新たにコンポーネントをレンダリングするNext.jsのレンダリング戦略です。
サーバーコンポーネントは、次の2つの条件のいずれかを満たすと自動的にDynamic Renderingに切り替わります。
- Dynamic APIの使用
- fetchオプションに{ cache: 'no-store' }が見つかる
レンダリング戦略決定表
| Dynamic APIの使用 | データキャッシング | レンダリング結果 | 
|---|---|---|
| ❌ いいえ | ✅ キャッシュあり | Static | 
| ✅ はい | ✅ キャッシュあり | Dynamic | 
| ❌ いいえ | ❌ キャッシュなし | Dynamic | 
| ✅ はい | ❌ キャッシュなし | Dynamic | 
つまり、Dynamic APIを使用せず、fetchにキャッシングを適用した場合のみStatic Renderingとなります。
Dynamic APIs
Dynamic APIとは、リクエスト時点のコンテキストに依存するAPIを指します。主なDynamic APIを見てみましょう。
1. cookies()
import { cookies } from 'next/headers';
async function Component() {
  const cookieStore = cookies();
  const theme = cookieStore.get('theme');
  return <div>Current theme: {theme?.value}</div>;
}2. headers()
import { headers } from 'next/headers';
async function Component() {
  const headersList = headers();
  const userAgent = headersList.get('user-agent');
  return <div>Accessing from: {userAgent}</div>;
}3. searchParams
// app/page.tsx
export default function Page({
  searchParams,
}: {
  searchParams: { query: string };
}) {
  return <div>Search query: {searchParams.query}</div>;
}Parallel Routesと併用する際の注意点
以下の例のようにsearchParamsを使用してモーダルを制御する場合、Dynamic Renderingによる問題が発生する可能性があります。
- searchParamsの変更時にサーバーコンポーネントがリレンダリングされる
- データフェッチによるモーダルの遅延
- モーダルの開閉と同時にデータが変更される
// app/@modal/default.tsx
'use client'
import {useSearchParams} from "next/navigation";
import {Modal} from "@/app/@modal/(.)post/[id]/modal";
function Default() {
  const searchParam = useSearchParams();
  const isModalOpen = searchParam.get('modal') === 'true';
  if (!isModalOpen) {
    return null;
  }
  return (
    <Modal>modal!!</Modal>
  );
}
export default Default;
解決策
最も簡単で効果的な方法は、Next.jsのルーティングシステムを迂回し、ブラウザの組み込みHistory APIを直接使用することです。
'use client';
function ProductList() {
  const openModal = () => {
    // Next.jsのルーティングを経由せずにURLを直接変更
    history.pushState(null, '', `?modal=true`);
  };
  return (
    <div>
      <button onClick={openModal}>モーダルを開く</button>
    </div>
  );
}LinkやuseRouterを使用すると、変更されたURLに対するナビゲーションが発生し、新たにレンダリングロジックが実行されます。しかし、History APIを使用するとNextのルーティングシステムを経由せずにURLのみを変更するため、レンダリングロジックは実行されませんが、NextではuseSearchParamsやusePathnameとの同期をサポートしているため、URL変更を検知してモーダルを表示することができます。
まとめ
サーバーコンポーネントはNext.jsにおいて重要なレンダリング最適化戦略ですが、誤って使用すると意図しないリレンダリングやパフォーマンス低下を引き起こす可能性があります。サーバーコンポーネントを効果的に使用するには、これらのレンダリング戦略をよく理解し、適切に活用することが重要だと思われます。

 によって翻訳されました。誤訳があれば教えてください!
によって翻訳されました。誤訳があれば教えてください!