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
文の中から呼び出さているため、条件付きでコンテクストから値を読み取ることができます。
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 つです。
エラーバウンダリを使ってユーザにエラーを表示する
プロミスが拒否されたときにユーザにエラーを表示したい場合は、エラーバウンダリ を使用できます。エラーバウンダリを使用するには、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);
// ...