React 19 で正式に加わった use は、Promise や Context の値をコンポーネントの中で読み取るための新しい API です。名前は「フック」に似ていますが、これまでのフックとは違って条件分岐やループの中でも呼べるという特徴があります。この記事では、use がどんなときに役立つのか、Promise を読む例と Context を読む例、useContext との違い、そして実際に使うときにつまずきやすいポイントまでを、具体的なコードとあわせて解説します。React で非同期データや共有状態を扱う書き方をすっきりさせたい方に向けた内容です。
目次
use は何を読み取るための API か
use は、Promise や Context といった「リソース」の値をコンポーネントの中で読み取るための API です。引数に Promise を渡すとその解決値を返し、Context を渡すとその現在の値を返します。React 19 で正式化され、非同期データの読み取りと Context の読み取りという2つの用途を1つの API でまかなえるようになりました。
最大の特徴は、通常のフックと違って呼び出せる場所の制約がゆるいことです。useState や useEffect はコンポーネントのトップレベルでしか呼べず、if 文やループの中に書くとルール違反になります。これに対して use は、条件分岐やループの中でも呼び出せます。そのため「ある条件のときだけ Context を読む」といった書き方が自然にできます。ただし、あくまでコンポーネントやカスタムフックの内部で呼ぶ点は変わりません。
Promise を読み取ってデータを表示する
まずは use で Promise を読み取る例を見てみます。基本的な流れは、Server Component(サーバー側で動くコンポーネント)でデータ取得の Promise を作り、それを props として Client Component(クライアント側で動くコンポーネント)へ渡し、受け取った側で use(promise) を呼んで解決値を取り出す、というものです。ここでは投稿一覧を表示する例で考えます。
先に Server Component 側です。fetch の結果を await せず、Promise のまま子コンポーネントへ渡すのがポイントです。
import { Suspense } from "react";
import Comments from "./Comments";
export default function Page() {
// await せず、Promise のまま渡すのがポイント
const commentsPromise = fetch("https://example.com/api/comments").then(
(res) => res.json()
);
return (
<Suspense fallback={<p>読み込み中...</p>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
);
}
次に、Promise を受け取る Client Component 側です。ファイル先頭に "use client" を付け、use(commentsPromise) で解決値を読み取ります。use は Promise が解決されるまでコンポーネントを一時停止(サスペンド)させ、その間は上位の Suspense の fallback が表示されます。
"use client";
import { use } from "react";
type Comment = { id: number; text: string };
export default function Comments({
commentsPromise,
}: {
commentsPromise: Promise<Comment[]>;
}) {
// Promise が解決されるまでサスペンドし、解決後に値が返る
const comments = use(commentsPromise);
return (
<ul>
{comments.map((comment) => (
<li key={comment.id}>{comment.text}</li>
))}
</ul>
);
}
この構成では、データ取得中は「読み込み中…」が表示され、取得が終わるとコメント一覧に切り替わります。use を使うと、useEffect と useState でローディング状態を自前で管理するような書き方をせずに、Promise の解決値をそのまま同期的に扱っているかのように書ける点が魅力です。
読み込みとエラーを Suspense と Error Boundary で扱う
use で Promise を読むときの、待機中とエラー時の扱いを整理しておきます。読み込み中の表示は先ほどのように Suspense の fallback が担当します。一方、Promise が失敗(reject)したときのエラー表示は、use を呼ぶコンポーネントを Error Boundary(エラー境界)で囲んで受け止めます。use は Promise が reject するとそのエラーを throw するため、上位の Error Boundary がそれをキャッチしてフォールバック UI を表示できる仕組みです。
import { Suspense } from "react";
import { ErrorBoundary } from "react-error-boundary";
import Comments from "./Comments";
export default function Page() {
const commentsPromise = fetch("https://example.com/api/comments").then(
(res) => res.json()
);
return (
// エラー時は ErrorBoundary、読み込み中は Suspense が担当する
<ErrorBoundary fallback={<p>読み込みに失敗しました</p>}>
<Suspense fallback={<p>読み込み中...</p>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
</ErrorBoundary>
);
}
このように Suspense と Error Boundary を組み合わせれば、「読み込み中」「成功」「失敗」の3つの状態を、それぞれの担当箇所に分けてすっきり書けます。ローディング用のフラグやエラー用の state をコンポーネント内に持たなくてよいのが、従来の書き方との大きな違いです。
Context を読み取る
use は Context の値を読み取る用途にも使えます。use(ThemeContext) と書くと、useContext(ThemeContext) と同じように、最も近い Provider が渡している値を返します。基本的な動きは useContext と変わりません。
"use client";
import { use } from "react";
import { ThemeContext } from "./ThemeContext";
export default function Button({ children }: { children: React.ReactNode }) {
// useContext(ThemeContext) と同じように現在の値を読み取れる
const theme = use(ThemeContext);
return <button className={theme}>{children}</button>;
}
では useContext があるのに、なぜ use でも Context を読めるようにしたのでしょうか。その答えが、先に触れた「呼び出せる場所の柔軟さ」です。use なら、条件分岐の中で必要なときだけ Context を読む、といった書き方ができます。
"use client";
import { use } from "react";
import { ThemeContext } from "./ThemeContext";
export default function Panel({ show }: { show: boolean }) {
if (show) {
// if の中で呼んでも問題ない。useContext ではルール違反になる書き方
const theme = use(ThemeContext);
return <div className={theme}>パネルの中身</div>;
}
return null;
}
同じことを useContext でやろうとして if の中に useContext を書くと、フックのルール(トップレベルでのみ呼ぶ)に反してしまいます。早期 return したあとに Context を読みたい、といった場面では use が便利です。
useContext との違い
use で Context を読む場合と useContext の違いを表にまとめます。値の読み取り自体は同じ結果になりますが、呼び出せる場所と扱える対象が異なります。
| 観点 | use(Context) | useContext(Context) |
|---|---|---|
| 読み取る値 | Context の現在の値 | Context の現在の値 |
| 条件分岐・ループ内での呼び出し | 可能 | 不可(トップレベルのみ) |
| Promise の読み取り | 可能 | 不可(Context 専用) |
| 登場時期 | React 19 で正式化 | 以前から利用可能 |
単純に Context を読むだけで、トップレベルで呼べば足りる場面では、これまでどおり useContext を使っても構いません。条件によって読むかどうかを変えたい場合や、Promise の読み取りとまとめて扱いたい場合に use を選ぶ、と考えると整理しやすいです。
Promise を毎レンダー作り直さないようにする
use で一番つまずきやすいのが、Promise の作り方です。Client Component の中で直接 fetch して作った Promise を use に渡すと、レンダーのたびに新しい Promise が生成され、その解決を待って再レンダー、また新しい Promise が生成される、という無限ループに陥りがちです。use に渡す Promise は、レンダーをまたいで同じものを指し続ける必要があります。
基本の対処は、Promise を Server Component 側で作ってから props で渡すことです。前半の例のように、サーバー側で fetch(...) の Promise を作って Client Component へ渡せば、クライアント側で毎回作り直す問題は起きません。クライアント側でどうしても Promise を用意する必要がある場合は、レンダーごとに作らないよう、キャッシュの仕組みを使って同じ Promise を返すようにします。次は避けたい書き方の例です。
"use client";
import { use } from "react";
export default function NG() {
// レンダーのたびに新しい Promise が作られてしまう(NG)
const data = use(fetch("/api/data").then((res) => res.json()));
return <p>{data.title}</p>;
}
この書き方だと、レンダーのたびに fetch が呼ばれて別の Promise になり、状態が安定しません。Promise は「レンダーの外」で作って安定した参照を保つ、と覚えておくとよいでしょう。基本はサーバー側やデータ取得ライブラリのキャッシュに任せ、コンポーネント本体では出来上がった Promise を use で読むだけ、という役割分担にするのが安全です。
もう1つ注意したいのは、use がまだ発展途上の API だという点です。正式化は React 19 なので、それより前のバージョンや、対応していない環境では利用できません。導入する際は、使っている React のバージョンとフレームワークが use に対応しているかを先に確認してください。
まとめ
use は、Promise や Context の値をコンポーネントの中で読み取る React 19 の新しい API です。Promise を渡せば解決値を同期的に扱うように書け、Suspense で読み込み中、Error Boundary でエラーを受け止められます。Context を渡せば useContext と同じ値を読めますが、条件分岐やループの中でも呼べる柔軟さがあるのが違いです。一方で、use に渡す Promise はレンダーごとに作り直さないことが重要で、基本は Server Component 側やキャッシュで作った安定した Promise を渡します。まだ発展途上の API なので、対応バージョンを確認したうえで、非同期データや共有状態の読み取りをすっきり書きたい場面から取り入れてみてください。