サーバーコンポーネントとクライアントコンポーネント
サーバーコンポーネントとクライアントコンポーネントの仕組みを理解するには、以下の2つの基本的なWeb概念に慣れておくと役立ちます:
サーバーとクライアント環境
Webアプリケーションの文脈において:

- クライアントとは、ユーザーのデバイス上のブラウザを指し、アプリケーションコードを要求するためにサーバーにリクエストを送信します。その後、サーバーから受け取ったレスポンスをユーザーが操作できるインターフェースに変換します。
- サーバーとは、データセンター内のコンピュータを指し、アプリケーションコードを保存し、クライアントからのリクエストを受け取り、何らかの計算を行い、適切なレスポンスを返します。
各環境には独自の機能と制約があります。例えば、レンダリングとデータ取得をサーバーに移行することで、クライアントに送信されるコード量を減らすことができ、アプリケーションのパフォーマンスを向上させることができます。しかし、先に学んだように、UIをインタラクティブにするためには、クライアント側でDOMを更新する必要があります。
したがって、サーバーとクライアント用に書くコードは常に同じではありません。データ取得やユーザー状態の管理など、特定の操作は、一方の環境にもう一方よりも適している場合があります。
ネットワーク境界
ネットワーク境界とは、異なる環境を分離する概念的な線です。
Reactでは、コンポーネントツリー内のどこにネットワーク境界を配置するかを選択できます。例えば、サーバー上でデータを取得してユーザーの投稿をレンダリングし(サーバーコンポーネントを使用)、各投稿のインタラクティブなLikeButton
をクライアント上でレンダリング(クライアントコンポーネントを使用)できます。
同様に、サーバー上でレンダリングされページ間で共有されるNav
コンポーネントを作成できますが、リンクのアクティブ状態を表示したい場合は、Links
のリストをクライアント上でレンダリングできます。

内部的には、コンポーネントは2つのモジュールグラフに分割されます。**サーバーモジュールグラフ(またはツリー)**にはサーバー上でレンダリングされるすべてのサーバーコンポーネントが含まれ、**クライアントモジュールグラフ(またはツリー)**にはすべてのクライアントコンポーネントが含まれます。
サーバーコンポーネントがレンダリングされた後、**Reactサーバーコンポーネントペイロード(RSC)**と呼ばれる特殊なデータ形式がクライアントに送信されます。RSCペイロードには以下が含まれます:
- サーバーコンポーネントのレンダリング結果
- クライアントコンポーネントをレンダリングする場所のプレースホルダー(または穴)と、それらのJavaScriptファイルへの参照
Reactはこの情報を使用して、サーバーコンポーネントとクライアントコンポーネントを統合し、クライアント上のDOMを更新します。
この仕組みを見てみましょう。
クライアントコンポーネントの使用
前章で学んだように、Next.jsはデフォルトでサーバーコンポーネントを使用します - これはアプリケーションのパフォーマンスを向上させるためであり、追加の手順を踏むことなく採用できることを意味します。
ブラウザのエラーを振り返ると、Next.jsはサーバーコンポーネント内でuseState
を使用しようとしていることを警告しています。インタラクティブな「いいね」ボタンをクライアントコンポーネントに移動することで、この問題を修正できます。
app
フォルダ内にlike-button.js
という新しいファイルを作成し、LikeButton
コンポーネントをエクスポートします:
export default function LikeButton() {}
<button>
要素とhandleClick()
関数をpage.js
から新しいLikeButton
コンポーネントに移動します:
export default function LikeButton() {
function handleClick() {
setLikes(likes + 1);
}
return <button onClick={handleClick}>Like ({likes})</button>;
}
次に、likes
ステートとインポートを移動します:
import { useState } from 'react';
export default function LikeButton() {
const [likes, setLikes] = useState(0);
function handleClick() {
setLikes(likes + 1);
}
return <button onClick={handleClick}>Like ({likes})</button>;
}
ここで、LikeButton
をクライアントコンポーネントにするために、ファイルの先頭にReactの'use client'
ディレクティブを追加します。これにより、Reactはこのコンポーネントをクライアント上でレンダリングします。
'use client';
import { useState } from 'react';
export default function LikeButton() {
const [likes, setLikes] = useState(0);
function handleClick() {
setLikes(likes + 1);
}
return <button onClick={handleClick}>Like ({likes})</button>;
}
page.js
ファイルに戻り、LikeButton
コンポーネントをページにインポートします:
import LikeButton from './like-button';
function Header({ title }) {
return <h1>{title ? title : 'Default title'}</h1>;
}
export default function HomePage() {
const names = ['Ada Lovelace', 'Grace Hopper', 'Margaret Hamilton'];
return (
<div>
<Header title="Develop. Preview. Ship." />
<ul>
{names.map((name) => (
<li key={name}>{name}</li>
))}
</ul>
<LikeButton />
</div>
);
}
両方のファイルを保存し、ブラウザでアプリを表示します。エラーがなくなったので、変更を加えて保存すると、ブラウザが自動的に更新されて変更が反映されるはずです。
この機能はFast Refreshと呼ばれます。編集内容について即座にフィードバックが得られ、Next.jsに事前設定されています。
まとめ
要約すると、サーバーとクライアント環境について学び、それぞれの使用タイミングを理解しました。また、Next.jsがパフォーマンス向上のためにデフォルトでReactサーバーコンポーネントを使用すること、およびUIの小さな部分をインタラクティブにするためにクライアントコンポーネントを選択的に使用する方法について学びました。
追加資料
サーバーコンポーネントとクライアントコンポーネントについては、さらに学ぶことがたくさんあります。以下に追加のリソースを示します: