use - This feature is available in the latest Canary

Canary

use フックは現在、React の Canary および experimental チャンネルでのみ利用可能です。React のリリースチャンネルについてはこちらをご覧ください。

use はプロミス (Promise) やコンテクストなどのリソースから値を読み取るための React フックです。

const value = use(resource);

リファレンス

use(resource)

コンポーネント内で use を呼び出し、プロミスやコンテクストなどのリソースから値を読み取ります。

import { use } from 'react';

function MessageComponent({ messagePromise }) {
const message = use(messagePromise);
const theme = use(ThemeContext);
// ...

他のあらゆる React フックとは異なり、use は if のようなループや条件文内でも呼び出すことができます。他の React フックと同様に、use を呼び出す関数はコンポーネントまたはフックでなければなりません。

プロミスを引数にして呼び出した場合、use フックは Suspense やエラーバウンダリ (error boundary) と協調して動作します。use を呼び出すコンポーネントは、use に渡されたプロミスが保留中 (pending) である間、サスペンド (suspend) します。use を呼び出すコンポーネントがサスペンスバウンダリでラップされている場合、フォールバックが表示されます。プロミスが解決 (resolve) された時点で、サスペンスフォールバックは、use フックから返されたデータを使用してレンダーされたコンポーネントの内容に置き換わります。use に渡されたプロミスが拒否 (reject) されると、最も近いエラーバウンダリのフォールバックが表示されます。

さらに例を見る

引数

返り値

use フックは、プロミスの解決された値やコンテクストなど、リソースから読み取った値を返します。

注意点

  • use フックは、コンポーネントまたは他のフック内で呼び出す必要があります。
  • サーバコンポーネントでデータをフェッチする際は、use よりも async と await を優先して使用してください。async と await は await が呼び出された地点からレンダーを再開しますが、use はデータが解決した後にコンポーネントを最初からレンダーします。
  • クライアントコンポーネントでプロミスを作成するよりも、サーバコンポーネントでプロミスを作成してそれをクライアントコンポーネントに渡すようにしてください。クライアントコンポーネントで作成されたプロミスは、レンダーごとに再作成されます。サーバコンポーネントからクライアントコンポーネントに渡されたプロミスは、再レンダー間で不変です。こちらの例を参照してください。

使用法

use でコンテクストを読み取る

コンテクストが use に渡された場合、useContext と同様に動作します。useContext はコンポーネントのトップレベルで呼び出す必要がありますが、use は if や for などの条件式の中でも呼び出すことができます。use はより柔軟であるため、useContext よりも優先的に使用してください。

import { use } from 'react';

function Button() {
const theme = use(ThemeContext);
// ...

use は、渡したコンテクストの値を返します。コンテクストの値を決定するために、React はコンポーネントツリーを上方向に検索し、当該コンテクストに対応する最も近いコンテクストプロバイダ (context provider) を見つけます。

Button にコンテクストを渡すには、それまたはその親コンポーネントのいずれかを、対応するコンテクストプロバイダでラップします。

function MyPage() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
);
}

function Form() {
// ... renders buttons inside ...
}

プロバイダと Button の間に何層のコンポーネントがあっても問題ありません。Form の内部のどこかで Button が use(ThemeContext) を呼び出すと、値として "dark" を受け取ることになります。

useContext とは異なり、use は if などの条件式やループの中で呼び出すことができます。

function HorizontalRule({ show }) {
if (show) {
const theme = use(ThemeContext);
return <hr className={theme} />;
}
return false;
}

use は if 文の中から呼び出さているため、条件付きでコンテクストから値を読み取ることができます。

落とし穴

useContext と同様に、use(context) は常にそれを呼び出しているコンポーネントの上側にある最も近いコンテクストプロバイダを探します。上方向に検索するため、use(context) を呼び出しているコンポーネント自体にあるコンテクストプロバイダは考慮されません。

import { createContext, use } from 'react';

const ThemeContext = createContext(null);

export default function MyApp() {
  return (
    <ThemeContext.Provider value="dark">
      <Form />
    </ThemeContext.Provider>
  )
}

function Form() {
  return (
    <Panel title="Welcome">
      <Button show={true}>Sign up</Button>
      <Button show={false}>Log in</Button>
    </Panel>
  );
}

function Panel({ title, children }) {
  const theme = use(ThemeContext);
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      <h1>{title}</h1>
      {children}
    </section>
  )
}

function Button({ show, children }) {
  if (show) {
    const theme = use(ThemeContext);
    const className = 'button-' + theme;
    return (
      <button className={className}>
        {children}
      </button>
    );
  }
  return false
}

サーバからクライアントへのデータストリーミング

サーバコンポーネントからクライアントコンポーネント に props としてプロミスを渡すことで、サーバからクライアントにデータをストリーミングすることができます。

import { fetchMessage } from './lib.js';
import { Message } from './message.js';

export default function App() {
const messagePromise = fetchMessage();
return (
<Suspense fallback={<p>waiting for message...</p>}>
<Message messagePromise={messagePromise} />
</Suspense>
);
}

クライアントコンポーネント は、受け取ったプロミス を use フックに渡します。これによりクライアントコンポーネントは、サーバコンポーネントが最初に作成したプロミスから値を読み取ることができます。

// message.js
'use client';

import { use } from 'react';

export function Message({ messagePromise }) {
const messageContent = use(messagePromise);
return <p>Here is the message: {messageContent}</p>;
}

Message は Suspense でラップされているため、プロミスが解決されるまでフォールバックが表示されます。プロミスが解決されると、その値が use フックによって読み取られ、Message コンポーネントがサスペンスフォールバックを置き換えます。

"use client";

import { use, Suspense } from "react";

function Message({ messagePromise }) {
  const messageContent = use(messagePromise);
  return <p>Here is the message: {messageContent}</p>;
}

export function MessageContainer({ messagePromise }) {
  return (
    <Suspense fallback={<p>⌛Downloading message...</p>}>
      <Message messagePromise={messagePromise} />
    </Suspense>
  );
}

補足

サーバコンポーネントからクライアントコンポーネントにプロミスを渡す場合、その解決値は、サーバとクライアント間でやりとり可能になるよう、シリアライズ可能でなければなりません。関数のようなデータ型はシリアライズ可能ではないため、プロミスの解決値として利用できません。

さらに深く知る

プロミスをサーバコンポーネントで解決するか、クライアントコンポーネントで解決するか?

プロミスはサーバコンポーネントからクライアントコンポーネントに渡し、use フックを使ってクライアントコンポーネントで解決することができます。また、await を使ってサーバコンポーネント側でプロミスを解決し、必要なデータを props としてクライアントコンポーネントに渡すことも可能でしょう。

export default function App() {
const messageContent = await fetchMessage();
return <Message messageContent={messageContent} />
}

しかしサーバコンポーネントで await を使用すると、await 文が終了するまでそのレンダーがブロックされます。サーバコンポーネントからクライアントコンポーネントにプロミスを渡すことで、プロミスがサーバコンポーネントのレンダーをブロックすることを防ぐことができます。

拒否されたプロミスの取り扱い

場合によっては、use に渡されたプロミスが拒否されることがあります。プロミスが拒否された場合にそれを処理する方法は以下の 2 つです。

  1. エラーバウンダリを使ってユーザにエラーを表示する
  2. Promise.catch で代替値を提供する

落とし穴

use は try-catch ブロック内で呼び出すことはできません。try-catch ブロックを使う代わりに、コンポーネントをエラーバウンダリでラップするか、またはプロミスの .catch メソッドで代替値を提供してください。

エラーバウンダリを使ってユーザにエラーを表示する

プロミスが拒否されたときにユーザにエラーを表示したい場合は、エラーバウンダリ を使用できます。エラーバウンダリを使用するには、use フックを呼び出しているコンポーネントをエラーバウンダリでラップします。use に渡されたプロミスが拒否されると、エラーバウンダリに書かれたフォールバックが表示されます。

"use client";

import { use, Suspense } from "react";
import { ErrorBoundary } from "react-error-boundary";

export function MessageContainer({ messagePromise }) {
  return (
    <ErrorBoundary fallback={<p>⚠️Something went wrong</p>}>
      <Suspense fallback={<p>⌛Downloading message...</p>}>
        <Message messagePromise={messagePromise} />
      </Suspense>
    </ErrorBoundary>
  );
}

function Message({ messagePromise }) {
  const content = use(messagePromise);
  return <p>Here is the message: {content}</p>;
}

Promise.catch で代替値を提供する

use に渡されたプロミスが拒否されたときに代替値を提供したい場合、プロミスの catch メソッドを使用できます。

import { Message } from './message.js';

export default function App() {
const messagePromise = new Promise((resolve, reject) => {
reject();
}).catch(() => {
return "no new message found.";
});

return (
<Suspense fallback={<p>waiting for message...</p>}>
<Message messagePromise={messagePromise} />
</Suspense>
);
}

プロミスの catch メソッドを使用するには、プロミスオブジェクトの catch を呼び出します。catch はエラーメッセージを引数とする関数を唯一の関数として受け取ります。catch に渡された関数によって返された任意の値が、プロミスの解決値として使用されます。


トラブルシューティング

“Suspense Exception: This is not a real error!”

あなたは React コンポーネントまたはフック関数の外部で use を呼び出しているか、または try-catch ブロック内で use を呼び出しています。try-catch ブロック内で use を呼び出している場合は、コンポーネントをエラーバウンダリでラップするか、プロミスの catch を呼び出してエラーをキャッチし、別の値でプロミスを解決します。こちらの例を参照してください。

React コンポーネントまたはフック関数の外部で use を呼び出している場合は、use の呼び出しを React コンポーネントまたはフック関数に移動します。

function MessageComponent({messagePromise}) {
function download() {
// ❌ the function calling `use` is not a Component or Hook
const message = use(messagePromise);
// ...

上記の場合、コンポーネントのクロージャの外で use を呼び出すようにすることで、コンポーネントまたはフックから use を呼び出すという条件を満たすようになります。

function MessageComponent({messagePromise}) {
// ✅ `use` is being called from a component.
const message = use(messagePromise);
// ...