| title | use |
|---|
use はプロミス (Promise) やコンテクストなどのリソースから値を読み取るための React API です。
const value = use(resource);コンポーネント内で use を呼び出し、プロミスやコンテクストなどのリソースから値を読み取ります。
import { use } from 'react';
function MessageComponent({ messagePromise }) {
const message = use(messagePromise);
const theme = use(ThemeContext);
// ...React フックとは異なり、use は if のようなループや条件文内でも呼び出すことができます。ただし React フックと同様に、use を呼び出す関数はコンポーネントまたはフック内でなければなりません。
プロミスを引数にして呼び出した場合、use API は Suspense やエラーバウンダリ (Error Boundary) と協調して動作します。use を呼び出すコンポーネントは、use に渡されたプロミスが保留中 (pending) である間、サスペンド (suspend) します。use を呼び出すコンポーネントがサスペンスバウンダリでラップされている場合、フォールバックが表示されます。プロミスが解決 (resolve) された時点で、サスペンスフォールバックは、use API から返されたデータを使用してレンダーされたコンポーネントの内容に置き換わります。use に渡されたプロミスが拒否 (reject) されると、最も近いエラーバウンダリのフォールバックが表示されます。
use API は、プロミスの解決された値やコンテクストなど、リソースから読み取った値を返します。
useAPI は、コンポーネントまたは他のフック内で呼び出す必要があります。- サーバコンポーネントでデータをフェッチする際は、
useよりもasyncとawaitを優先して使用してください。asyncとawaitはawaitが呼び出された地点からレンダーを再開しますが、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 value="dark">
<Form />
</ThemeContext>
);
}
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 value="dark">
<Form />
</ThemeContext>
)
}
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
}.panel-light,
.panel-dark {
border: 1px solid black;
border-radius: 4px;
padding: 20px;
}
.panel-light {
color: #222;
background: #fff;
}
.panel-dark {
color: #fff;
background: rgb(23, 32, 42);
}
.button-light,
.button-dark {
border: 1px solid #777;
padding: 5px;
margin-right: 10px;
margin-top: 10px;
}
.button-dark {
background: #222;
color: #fff;
}
.button-light {
background: #fff;
color: #222;
}サーバコンポーネントからクライアントコンポーネント に 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 API に渡します。これによりクライアントコンポーネントは、サーバコンポーネントが最初に作成したプロミスから値を読み取ることができます。
// 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 API によって読み取られ、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>
);
}import { useState } from "react";
import { MessageContainer } from "./message.js";
function fetchMessage() {
return new Promise((resolve) => setTimeout(resolve, 1000, "⚛️"));
}
export default function App() {
const [messagePromise, setMessagePromise] = useState(null);
const [show, setShow] = useState(false);
function download() {
setMessagePromise(fetchMessage());
setShow(true);
}
if (show) {
return <MessageContainer messagePromise={messagePromise} />;
} else {
return <button onClick={download}>Download message</button>;
}
}import React, { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './styles.css';
// TODO: update this example to use
// the Codesandbox Server Component
// demo environment once it is created
import App from './App';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<App />
</StrictMode>
);サーバコンポーネントからクライアントコンポーネントにプロミスを渡す場合、その解決値は、サーバとクライアント間でやりとり可能になるよう、シリアライズ可能でなければなりません。関数のようなデータ型はシリアライズ可能ではないため、プロミスの解決値として利用できません。
プロミスはサーバコンポーネントからクライアントコンポーネントに渡し、use API を使ってクライアントコンポーネントで解決することができます。また、await を使ってサーバコンポーネント側でプロミスを解決し、必要なデータを props としてクライアントコンポーネントに渡すことも可能でしょう。
export default async function App() {
const messageContent = await fetchMessage();
return <Message messageContent={messageContent} />
}しかしサーバコンポーネントで await を使用すると、await 文が終了するまでそのレンダーがブロックされます。サーバコンポーネントからクライアントコンポーネントにプロミスを渡すことで、プロミスがサーバコンポーネントのレンダーをブロックすることを防ぐことができます。
場合によっては、use に渡されたプロミスが拒否されることがあります。プロミスが拒否された場合にそれを処理する方法は以下の 2 つです。
プロミスが拒否されたときにユーザにエラーを表示したい場合は、エラーバウンダリ を使用できます。エラーバウンダリを使用するには、use API を呼び出しているコンポーネントをエラーバウンダリでラップします。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>;
}import { useState } from "react";
import { MessageContainer } from "./message.js";
function fetchMessage() {
return new Promise((resolve, reject) => setTimeout(reject, 1000));
}
export default function App() {
const [messagePromise, setMessagePromise] = useState(null);
const [show, setShow] = useState(false);
function download() {
setMessagePromise(fetchMessage());
setShow(true);
}
if (show) {
return <MessageContainer messagePromise={messagePromise} />;
} else {
return <button onClick={download}>Download message</button>;
}
}import React, { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './styles.css';
// TODO: update this example to use
// the Codesandbox Server Component
// demo environment once it is created
import App from './App';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<App />
</StrictMode>
);{
"dependencies": {
"react": "19.0.0",
"react-dom": "19.0.0",
"react-scripts": "^5.0.0",
"react-error-boundary": "4.0.3"
},
"main": "/index.js"
}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 に渡された関数によって返された任意の値が、プロミスの解決値として使用されます。
あなたは 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);
// ...