メインコンテンツまでスキップ

Next.js - React Essentials

Server Components

  • レンダリングされる場所をサーバ側に固定できるようになった
    • Client Components (及びこれまでの Pages Router)
      • pre-rendering & hydration で動作する。つまりどちらサイドでも描写される可能性がある。
      • 依然として JavaScript が必要
    • Server Components
      • サーバーサイドでのみレンダリングされる
      • クライアントサイドでレンダリングされることはない
      • JavaScript は完全に必要ない
  • Server Components にはレンダリングの種類が 2 つある
    • Static Rendering
      • ビルド時に確定するもの
      • SSG, ISR と同じ
      • キャッシュ可
    • Dynamic Rendering
      • リクエスト時に確定するもの
      • SSR(getServerSideProps())と同じ
      • キャッシュ不可
  • メリット
    • 速いデータ取得
    • バンドルサイズの減少
    • Ruby on Rails のような開発体験
  • App Router ではコンポーネントはデフォルトで Server Components としてレンダリングされる
  • 以下のファイル名は特別な役割を持つ
    • layout
    • page
    • loading
    • not-found
    • error
    • global-error
    • route
    • template
    • default

Client Components

  • インタラクティブ性をもつコンポーネント
  • pre-rendered & hydrated される
  • 従前の Next.js の Pages Router と同じ
  • use client directive
    • ファイルの先頭に書くと、Client Components としてレンダリングされる
    • Server のみでレンダリングするものと、Client でもレンダリングするものの境界を規定する
    • 配下のコンポーネントはすべて Client Components としてレンダリングされるようになる

使い分け

  • 以下を行う場合は Server Component が最適
    • データ取得
    • バックエンドへのアクセス
    • Access Token などの機密情報を扱う
    • 大容量の依存ライブラリを使う
  • 以下を行う場合は Client Component が最適
    • onClick, onChange 等のインタラクティブ性が必要
    • useState, useEffect などのライフサイクルエフェクトが必要
    • ブラウザ固有の API を使う
    • カスタムフックを使う
    • Class Component を使う

よくあるパターン

Client Component を葉に追いやる

そうすることで、Server Component の担当できる領域を増やしていく

Client Component と Server Component を組み合わせて使う

  • サーバーではすべての Server Component が事前にレンダリングされて Client に送られる
  • Client Component 内で Server Component をインポートして使うことはできない
    • より具体的には、祖先から子孫に下っていく過程で一つでもuse clientしている箇所があれば、その配下では Server Component を使うことはできない
  • ただし、Composition の書き方をすれば、Client Component の中に Server Component をネストさせることはできる
const SomeServerComponent = () => {
return (
<ExampleClientComponent>
<ExampleServerComponent />
</ExampleClientComponent>
);
};

Client Component に props を渡す

  • Server Component から Client Component に props を渡すときには Serializable な値しか渡せない
    • e.g. コールバック関数を与えることはできない
  • なぜならそこにネットワークによる境界(断絶)があるため
    • App Router では、Server Component と Client Component の間に境界がある
    • Pages Router では、get***Propsと Page Component の間に境界がある

サーバサイド or クライアントサイドでしか使えないコードを明示する

await fetch('https://external-service.com/data', {
headers: {
authorization: process.env.API_KEY,
},
});

例えば上記はNEXT_PUBLICがついていないのでクライアントサイドでは動作しない。 こういうコードを書くときは、以下のようにする。

npm install server-only
npm install client-only
import 'server-only';
// or
import 'server-only';

こうすることでサーバサイド or クライアントサイドでしか動作しないことを明示&保証できる。

Data Fetching

とくに理由がないなら、Server Component でデータ取得を行うのがオススメ

Third-party packages

client-onlyを書いていないライブラリが多くエラーになることが多いので、ラップすると良い。

Context

Server Component では Context を作ったり使ったりできないので、Composition を使った構成にする必要がある。

Client Component で Context をセットアップして ↓

'use client';

import { ThemeProvider } from 'acme-theme';
import { AuthProvider } from 'acme-auth';

export function Providers({ children }) {
return (
<ThemeProvider>
<AuthProvider>{children}</AuthProvider>
</ThemeProvider>
);
}

Server Component でそれをつかう ↓

import { Providers } from './providers';

export default function RootLayout({ children }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}

Server Components 間でのデータ共有

  • Context を使う必要はなく、単に普通の JS の世界の仕組みで共有すれば OK
    • シングルトンなど