コンテンツへスキップ

10

サーバーコンポーネントとクライアントコンポーネント

サーバーコンポーネントとクライアントコンポーネントがどのように機能するかを理解するために、2つの基本的なウェブの概念に慣れておくと役立ちます

  • アプリケーションコードを実行できる環境: サーバーとクライアント。
  • サーバーコードとクライアントコードを分離するネットワーク境界

サーバーとクライアントの環境

ウェブアプリケーションのコンテキストにおいて

Diagram showing a browser on the left and a server on the right, separated by a network boundary.
  • クライアントとは、ユーザーのデバイス上のブラウザを指し、アプリケーションコードのためにサーバーにリクエストを送信します。その後、サーバーから受け取った応答を、ユーザーが操作できるインターフェースに変換します。
  • サーバーとは、データセンターにあるコンピュータを指し、アプリケーションコードを保存し、クライアントからのリクエストを受け取り、何らかの計算を行い、適切な応答を返します。

各環境には、それぞれ独自の機能と制約があります。たとえば、レンダリングとデータ取得をサーバーに移動することで、クライアントに送信されるコードの量を減らし、アプリケーションのパフォーマンスを向上させることができます。しかし、以前に学んだように、UIをインタラクティブにするには、クライアントでDOMを更新する必要があります。

したがって、サーバーとクライアントのために書くコードは常に同じではありません。特定の操作(例:データ取得やユーザー状態の管理)は、どちらか一方の環境により適しています。

ネットワーク境界

ネットワーク境界は、異なる環境を分離する概念的な線です。

Reactでは、コンポーネントツリー内のどこにネットワーク境界を配置するかを選択します。たとえば、サーバーでデータを取得してユーザーの投稿をレンダリングし(サーバーコンポーネントを使用)、その後、各投稿のインタラクティブなLikeButtonをクライアントでレンダリングできます(クライアントコンポーネントを使用)。

同様に、サーバーでレンダリングされ、ページ間で共有されるNavコンポーネントを作成できますが、リンクのアクティブ状態を表示したい場合は、Linksのリストをクライアントでレンダリングできます。

A component tree showing a layout that has 3 components as its children: Nav, Page, and Footer. The page component has 2 children: Posts and LikeButton. The Posts component is rendered on the server, and the LikeButton component is rendered on the client.

舞台裏では、コンポーネントは2つのモジュールグラフに分割されます。サーバーモジュールグラフ(またはツリー)には、サーバーでレンダリングされるすべてのサーバーコンポーネントが含まれ、クライアントモジュールグラフ(またはツリー)には、すべてのクライアントコンポーネントが含まれます。

サーバーコンポーネントがレンダリングされた後、React Server Component Payload (RSC)と呼ばれる特殊なデータ形式がクライアントに送信されます。RSCペイロードには以下が含まれます。

  1. サーバーコンポーネントのレンダリング結果。
  2. クライアントコンポーネントがレンダリングされるべき場所のプレースホルダー(または穴)と、それらのJavaScriptファイルへの参照。

Reactはこの情報を使用して、サーバーコンポーネントとクライアントコンポーネントを統合し、クライアントでDOMを更新します。

これがどのように機能するか見てみましょう。

クライアントコンポーネントの使用

前のチャプターで学んだように、Next.jsはデフォルトでサーバーコンポーネントを使用します。これはアプリケーションのパフォーマンスを向上させるためであり、それらを採用するために追加の手順を踏む必要がないことを意味します。

ブラウザのエラーを振り返ると、Next.jsはサーバーコンポーネント内でuseStateを使用しようとしていることを警告しています。インタラクティブな「いいね」ボタンをクライアントコンポーネントに移動することで、この問題を解決できます。

appフォルダー内にlike-button.jsという新しいファイルを作成し、LikeButtonコンポーネントをエクスポートします

/app/like-button.js
export default function LikeButton() {}

<button>要素とhandleClick()関数をpage.jsから新しいLikeButtonコンポーネントに移動します

/app/like-button.js
export default function LikeButton() {
  function handleClick() {
    setLikes(likes + 1);
  }
 
  return <button onClick={handleClick}>Like ({likes})</button>;
}

次に、likesの状態とインポートを移動します

/app/like-button.js
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に指示します。

/app/like-button.js
'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コンポーネントをページにインポートします

/app/page.js
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のごく一部をインタラクティブにするためにクライアントコンポーネントをどのように選択できるかについても学びました。

補足資料

サーバーコンポーネントとクライアントコンポーネントについては、さらに多くのことを学ぶことができます。追加のリソースを以下に示します。

チャプター完了10

サーバーコンポーネントとクライアントコンポーネントの使用方法を学びました。

次へ

11: 次のステップ

次は何をしますか?