クライアントコンポーネント

クライアントコンポーネントを使用すると、リクエスト時にクライアント側でレンダリング可能なインタラクティブなUIを作成できます。Next.jsでは、クライアントレンダリングはオプトイン方式となっており、Reactがクライアント側でレンダリングすべきコンポーネントを明示的に指定する必要があります。

このページでは、クライアントコンポーネントの動作原理、レンダリング方法、および使用場面について説明します。

クライアントレンダリングの利点

クライアント側でレンダリングを行うことには、以下のような利点があります:

  • インタラクティブ性: クライアントコンポーネントはstate、エフェクト、イベントリスナーを使用できるため、ユーザーに即時フィードバックを提供し、UIを更新できます。
  • ブラウザAPIの利用: クライアントコンポーネントは位置情報APIlocalStorageなどのブラウザAPIにアクセス可能で、特定のユースケースに合わせたUIを構築できます。

Next.jsでのクライアントコンポーネントの使用

クライアントコンポーネントを使用するには、ファイルの先頭(import文の前)にReactの"use client"ディレクティブを追加します。

"use client"は、サーバーコンポーネントとクライアントコンポーネントモジュール間の境界を宣言するために使用されます。つまり、ファイル内で"use client"を定義すると、そのファイルにインポートされる他のすべてのモジュール(子コンポーネントを含む)はクライアントバンドルの一部と見なされます。

'use client'

import { useState } from 'react'

export default function Counter() {
  const [count, setCount] = useState(0)

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  )
}
'use client'

import { useState } from 'react'

export default function Counter() {
  const [count, setCount] = useState(0)

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  )
}

以下の図はネストされたコンポーネントを示しています。toggle.jsonClickuseStateを使用する場合、"use client"ディレクティブが定義されていないとエラーが発生します。これは、デフォルトではコンポーネントがサーバー側でレンダリングされるためで、これらのAPIはサーバー環境では利用できません。toggle.js"use client"ディレクティブを定義することで、コンポーネントとその子コンポーネントをクライアント側でレンダリングするようReactに指示できます。

Use Client Directive and Network Boundary

複数のuse clientエントリポイントの定義:

Reactコンポーネントツリー内に複数の"use client"エントリポイントを定義できます。これにより、アプリケーションを複数のクライアントバンドル(またはブランチ)に分割できます。

ただし、クライアント側でレンダリングする必要があるすべてのコンポーネントに"use client"を定義する必要はありません。境界を一度定義すれば、その配下のすべての子コンポーネントとインポートされたモジュールはクライアントバンドルの一部と見なされます。

クライアントコンポーネントのレンダリング方法

Next.jsでは、クライアントコンポーネントのレンダリング方法は、リクエストがフルページロード(アプリケーションへの初回訪問またはブラウザのリフレッシュによってトリガーされるページ再読み込み)か、それ以降のナビゲーションかによって異なります。

フルページロード時

初期ページロードを最適化するため、Next.jsはReactのAPIを使用して、クライアントコンポーネントとサーバーコンポーネントの両方に対してサーバー側で静的HTMLプレビューをレンダリングします。つまり、ユーザーが初めてアプリケーションにアクセスしたとき、クライアントがJavaScriptバンドルをダウンロード、解析、実行するのを待たずに、すぐにページのコンテンツを確認できます。

サーバー側では:

  1. Reactがサーバーコンポーネントを**React Server Component Payload (RSC Payload)**と呼ばれる特殊なデータ形式にレンダリングします。これにはクライアントコンポーネントへの参照が含まれます。
  2. Next.jsはRSC PayloadとクライアントコンポーネントのJavaScript命令を使用して、サーバー上でルートのHTMLをレンダリングします。

その後、クライアント側では:

  1. HTMLが使用され、ルートの非インタラクティブな初期プレビューが即座に表示されます。
  2. React Server Components Payloadが使用され、クライアントコンポーネントとサーバーコンポーネントのツリーが調整され、DOMが更新されます。
  3. JavaScript命令が使用され、クライアントコンポーネントがハイドレートされ、UIがインタラクティブになります。

ハイドレートとは?

ハイドレートとは、静的HTMLをインタラクティブにするためにDOMにイベントリスナーをアタッチするプロセスです。内部的には、ハイドレートはReactのhydrateRoot APIを使用して行われます。

以降のナビゲーション時

以降のナビゲーションでは、クライアントコンポーネントはサーバー側でレンダリングされたHTMLなしで、完全にクライアント側でレンダリングされます。

つまり、クライアントコンポーネントのJavaScriptバンドルがダウンロードされ、解析されます。バンドルの準備が整うと、ReactはRSC Payloadを使用してクライアントコンポーネントとサーバーコンポーネントのツリーを調整し、DOMを更新します。

サーバー環境への回帰

"use client"境界を宣言した後でも、サーバー環境に戻りたい場合があります。例えば、クライアントバンドルサイズを削減したい場合や、サーバー側でデータを取得したい場合、サーバーでのみ利用可能なAPIを使用したい場合などです。

クライアントコンポーネント内に理論的にネストされているコードでも、クライアントコンポーネントとサーバーコンポーネント、およびサーバーアクションを組み合わせることで、サーバー側に保持できます。詳細については、コンポジションパターンのページを参照してください。