'use client'

React Server Components

'use client'React Server Components 用の機能です。

'use client' を使い、どのコードがクライアントで実行されるかをマークします。


リファレンス

'use client'

ファイルのトップに 'use client' を加えることで、当該モジュールとそれが連動してインポートしている依存モジュールがクライアントコードであるとマークします。

'use client';

import { useState } from 'react';
import { formatDate } from './formatters';
import Button from './button';

export default function RichTextEditor({ timestamp, text }) {
const date = formatDate(timestamp);
// ...
const editButton = <Button />;
// ...
}

'use client' でマークされているファイルがサーバコンポーネントからインポートされた場合、互換性のあるバンドラは当該モジュールのインポートを、サーバで実行されるコードとクライアントで実行されるコードの境界として扱います。

上記では formatDateButtonRichTextEditor が依存するモジュールですので、これらのモジュール自体に 'use client' ディレクティブが含まれているかどうかに関わらず、これらもクライアントで評価されます。ある単一のモジュールが、サーバコードからインポートされた場合はサーバで、クライアントコードからインポートされた場合はクライアントで評価される場合があることに注意してください。

注意点

  • 'use client' はファイルの冒頭、すなわちインポートや他のコードより先になければなりません(コメントは OK です)。シングルクォートまたはダブルクォートで書かれていなければならず、バックティックは無効です。
  • 'use client' モジュールが別のクライアントレンダーされるモジュールからインポートされた場合、ディレクティブの効果はありません。
  • コンポーネントモジュールに 'use client' ディレクティブが含まれている場合、そのコンポーネントは必ずクライアントコンポーネントであることが保証されます。しかしコンポーネントに直接 'use client' ディレクティブがなくとも、クライアントで評価されることがあります。
    • コンポーネントがクライアントコンポーネントと見なされるのは、それが 'use client' ディレクティブを含むモジュールで定義されている場合、またはそれが 'use client' ディレクティブを含むモジュールの間接的な依存モジュールである場合です。それ以外の場合、サーバコンポーネントとなります。
  • クライアントで評価されるようマークされるコードとはコンポーネントに限りません。クライアントモジュールのサブツリーに含まれるすべてのコードは、クライアントに送信され、クライアントで実行されます。
  • サーバで評価されるモジュールが 'use client' のモジュールから値をインポートする場合、その値は React コンポーネントであるか、またはクライアントコンポーネントに渡せるようサポート済のシリアライズ可能な props の型のいずれかでなければなりません。それ以外の方法で使用すると例外がスローされます。

'use client' がクライアントコードをマークする方法

React アプリでは、コンポーネントはしばしば別々のファイル、すなわちモジュールに分割されます。

React Server Components を使用するアプリでは、デフォルトでアプリはサーバでレンダーされます。'use client'モジュール依存関係ツリーにサーバ・クライアント境界を導入、つまり実質的にはクライアントモジュールのサブツリーの作成を行います。

これをより具体的に示すために、以下の React Server Components アプリを考えてみましょう。

import FancyText from './FancyText';
import InspirationGenerator from './InspirationGenerator';
import Copyright from './Copyright';

export default function App() {
  return (
    <>
      <FancyText title text="Get Inspired App" />
      <InspirationGenerator>
        <Copyright year={2004} />
      </InspirationGenerator>
    </>
  );
}

このサンプルアプリのモジュール依存関係ツリーでは、InspirationGenerator.js に書かれた 'use client' ディレクティブが、当該モジュールとそのすべての間接的な依存モジュールをクライアントモジュールとしてマークします。これで InspirationGenerator.js から始まるサブツリー全体がクライアントモジュールとなるのです。

トップノードがモジュール 'App.js' を表す木構造のグラフ。'App.js'には 'Copyright.js'、'FancyText.js'、'InspirationGenerator.js' の 3 つの子ノードがある。'InspirationGenerator.js'には 'FancyText.js'と'inspirations.js' の 2 つの子ノードがある。'InspirationGenerator.js'を含む下のノードには黄色い背景色が付けられており、'InspirationGenerator.js'の 'use client' ディレクティブによってこのサブグラフがクライアント側でレンダーされることを示している。
トップノードがモジュール 'App.js' を表す木構造のグラフ。'App.js'には 'Copyright.js'、'FancyText.js'、'InspirationGenerator.js' の 3 つの子ノードがある。'InspirationGenerator.js'には 'FancyText.js'と'inspirations.js' の 2 つの子ノードがある。'InspirationGenerator.js'を含む下のノードには黄色い背景色が付けられており、'InspirationGenerator.js'の 'use client' ディレクティブによってこのサブグラフがクライアント側でレンダーされることを示している。

'use client' は React Server Components アプリのモジュール依存関係ツリーを分割し、InspirationGenerator.js とそのすべての依存モジュールをクライアントレンダーされるものとしてマークする

レンダー中、フレームワークはルートコンポーネントから始め、レンダーツリーを順にレンダーしていきますが、その際にクライアントとマークされたものからインポートされたコードの評価を選択的に除外します。

その後レンダーツリーのうちサーバでレンダーされた部分が、クライアントに送信されます。クライアントは、ダウンロードしたクライアントコードを用いて、ツリーの残りの部分のレンダーを完了します。

各ノードがコンポーネントを表し、その子要素を子コンポーネントとして表すツリーグラフ。トップレベルのノードは 'App' とラベル付けされ、2つの子コンポーネント 'InspirationGenerator' と 'FancyText' を持っています。'InspirationGenerator' は2つの子コンポーネント、'FancyText' と 'Copyright' を持っています。'InspirationGenerator' とその子コンポーネント 'FancyText' はクライアントレンダリングされるとマークされています。
各ノードがコンポーネントを表し、その子要素を子コンポーネントとして表すツリーグラフ。トップレベルのノードは 'App' とラベル付けされ、2つの子コンポーネント 'InspirationGenerator' と 'FancyText' を持っています。'InspirationGenerator' は2つの子コンポーネント、'FancyText' と 'Copyright' を持っています。'InspirationGenerator' とその子コンポーネント 'FancyText' はクライアントレンダリングされるとマークされています。

React Server Components アプリのレンダーツリー。InspirationGenerator とその子コンポーネント FancyText は、クライアントとマークされたコードからエクスポートされたコンポーネントであるため、クライアントコンポーネントと見なされる

ここで以下の定義を導入しましょう。

  • クライアントコンポーネントとは、レンダーツリーの中の、クライアントでレンダーされるコンポーネントです。
  • サーバコンポーネントとは、レンダーツリーの中の、サーバでレンダーされるコンポーネントです。

上記のサンプルアプリでは、AppFancyTextCopyright はすべてサーバでレンダーされるのでサーバコンポーネントと見なされます。InspirationGenerator.js とその間接的な依存モジュールはクライアントコードとしてマークされているため、コンポーネント InspirationGenerator とその子コンポーネントである FancyText はクライアントコンポーネントとなります。

さらに深く知る

FancyText がサーバコンポーネントでありクライアントコンポーネントでもある理由

上記の定義によれば、コンポーネント FancyText は、サーバコンポーネントでもありクライアントコンポーネントでもあることになります。どういうことでしょうか?

まずは “コンポーネント” という用語があまり厳密ではないことを明示的に意識しましょう。“コンポーネント” とは以下の 2 つの意味で使用されます。

  1. 「コンポーネント」はコンポーネントの定義を指すことがあります。ほとんどの場合、これは関数です。
// This is a definition of a component
function MyComponent() {
return <p>My Component</p>
}
  1. 「コンポーネント」はまた、その定義に従って使用されている個々のコンポーネントを指すこともあります。
import MyComponent from './MyComponent';

function App() {
// This is a usage of a component
return <MyComponent />;
}

ここが厳密でなくとも大抵の場合は概念を説明する際に問題とはなりませんが、この場合はそうではありません。

サーバコンポーネントやクライアントコンポーネントについて話すとき、コンポーネントの個々の使用法を指しています。

  • コンポーネントが 'use client' ディレクティブを含んだモジュールで定義されている場合や、コンポーネントが他のクライアントコンポーネントからインポートされコールされる場合、そのコンポーネントの使用法はクライアントコンポーネントであるということになります。
  • それ以外の場合、そのコンポーネントの使用法はサーバコンポーネントだということになります。
各ノードがコンポーネントを表し、その子供を子コンポーネントとして表すツリーグラフ。トップレベルのノードは 'App' とラベル付けされており、'InspirationGenerator' と 'FancyText' の 2 つの子コンポーネントを持っている。'InspirationGenerator' は 'FancyText' と 'Copyright' の 2 つの子コンポーネントを持っている。'InspirationGenerator' とその子コンポーネント 'FancyText' はクライアントでレンダリングされるとマークされている。
各ノードがコンポーネントを表し、その子供を子コンポーネントとして表すツリーグラフ。トップレベルのノードは 'App' とラベル付けされており、'InspirationGenerator' と 'FancyText' の 2 つの子コンポーネントを持っている。'InspirationGenerator' は 'FancyText' と 'Copyright' の 2 つの子コンポーネントを持っている。'InspirationGenerator' とその子コンポーネント 'FancyText' はクライアントでレンダリングされるとマークされている。
コンポーネントの「使用法」を表すのがレンダーツリー

FancyText の問題に戻りましょう。分かるのは、コンポーネントの定義として 'use client' ディレクティブがないこと、そして使用法として 2 種類あることです。

FancyTextApp の子として使用されている場合、その使用法はサーバコンポーネントです。FancyTextInspirationGenerator の下でインポートされて呼び出されている場合、InspirationGenerator'use client' ディレクティブが含まれているため、その FancyText の使用法はクライアントコンポーネントとなります。

つまり、FancyText のコンポーネント定義がサーバ上で評価される一方で、クライアントコンポーネントとして使用されるためクライアントにダウンロードもされる、ということになります。

さらに深く知る