第112章:【v19フック】use(Promise)
この章でできるようになること 🎯
- 「ネットからデータを取ってきて表示する」最新のやり方がわかる
- 新フック
use(Promise)のイメージがつかめる - かんたんなサンプルを自分で書ける 💻
1. use(Promise) ってなに?🌊
React v19 で入った新しい API use は、
「Promise や Context から値を“そのまま”取り出すためのフック」
みたいな存在です。Promise を渡すと、解決されたあとに中身の値を返してくれます。(react.dev)
ざっくりいうと、
useEffect + useStateでやってた 「データ取る → loading フラグ管理 → setState → エラー処理…」 みたいなゴチャゴチャをconst data = use(somePromise);の 1 行にギュッとまとめられるイメージです ✨(cybrosys.com)
しかも、Promise がまだ終わってないときはコンポーネントのレンダリングを一時停止(サスペンド)して、いちばん近くの <Suspense> の fallback を表示してくれます。(Qiita)
2. ざっくり動きのイメージ図 🧠
use(Promise) の裏側の流れを、かんたんに図で見てみます。
※図の
<Suspense>とか「サスペンド」という言葉は、次の章(113 以降)でガッツリやるので、ここでは「読み込み中の画面を出してくれる仕組み」くらいのイメージで OK 🙆♀️
3. useEffect との違いを 10 秒でおさらい ⏱️
前の章(111)では、こんな流れでしたよね:
useStateでdataとloadingを用意useEffectの中でfetch- 取れたら
setData、エラーならsetError - JSX の中で
loading ? '読み込み中...' : データ表示
use(Promise) だと、同じことをざっくりこう書けます👇
const data = use(somePromise);- 読み込み中の画面は
<Suspense fallback={<p>読み込み中…</p>}>におまかせ - エラーは「エラーバウンダリ」という仕組みでまとめて処理(これは 118〜120 章で)
「状態を自分で管理する」というより、 React に “待つこと” を丸投げする 感じです 🙌(cybrosys.com)
4. まずは超シンプルなサンプルを書いてみよう 🐶
最初は「細かいことは気にしないで、とりあえず動かしてみる」がいちばんです 👍 ここでは ランダムな犬画像 API を使って、「ワンちゃん 1 枚だけ表示する」サンプルを作ってみます。
- API:
https://dog.ceo/api/breeds/image/random - 返ってくる JSON(だいたいこんな形)
{
"message": "画像のURL",
"status": "success"
}
4-1. API 用のファイルを作る(src/dogApi.ts)📁
ポイントは 1 つだけ:
Promise を「コンポーネントの外」で作っておく (=モジュール読み込み時に 1 回だけ作る)
これで「毎回新しい Promise ができちゃって React が怒る問題」を避けられます 🧯(react.dev)
// src/dogApi.ts
export type DogApiResponse = {
message: string; // 画像URL
status: "success" | "error";
};
// モジュールが読み込まれたタイミングで 1 回だけ Promise を作っておく
export const randomDogImagePromise: Promise<DogApiResponse> = fetch(
"https://dog.ceo/api/breeds/image/random"
).then((res) => {
if (!res.ok) {
throw new Error("ワンちゃん画像の取得に失敗しました 🥲");
}
return res.json() as Promise<DogApiResponse>;
});
💡 実務では「毎回違う ID を指定したい」などでもう少し工夫が必要ですが、 最初の一歩としては「モジュール外に Promise を置く」 パターンを覚えておけば OK です。
4-2. use(Promise) を使うコンポーネント ✨(DogImage.tsx)
次に、React コンポーネントの中で use を呼んで、さっきの Promise から値を取り出します。
// src/DogImage.tsx
import { use } from "react";
import { randomDogImagePromise } from "./dogApi";
export function DogImage() {
// 👇 Promise の「中身」が data に入ってくる
const data = use(randomDogImagePromise);
if (data.status !== "success") {
// ここではざっくりエラーメッセージだけ
return <p>画像の取得に失敗しました… 🥲</p>;
}
return (
<div>
<h2>今日のワンちゃん 🐶</h2>
<img
src={data.message}
alt="かわいいワンちゃん"
style={{ maxWidth: "300px", borderRadius: "16px" }}
/>
</div>
);
}
ここでのポイント 📝
use(randomDogImagePromise)と書くだけで、DogApiResponse型のオブジェクトがdataに入る(TS 的にも安心)async/awaitもuseStateもuseEffectも書いてません ✨
4-3. <Suspense> で包んであげる(App.tsx)🛟
use(Promise) を使うコンポーネントは、どこかの親で <Suspense> に包んでおく必要があります。(react.dev)
App.tsx をこんな感じにしてみましょう。
// src/App.tsx
import { Suspense } from "react";
import { DogImage } from "./DogImage";
function App() {
return (
<main style={{ padding: "24px", fontFamily: "system-ui, sans-serif" }}>
<h1>React v19 データ取得デモ 🐾</h1>
{/* 👇 DogImage の中で use(Promise) を使っている */}
<Suspense fallback={<p>ワンちゃんを探してます… ⏳</p>}>
<DogImage />
</Suspense>
</main>
);
}
export default App;
これで流れはこうなります ✅
- 最初のレンダリングで
DogImageがuse(randomDogImagePromise)を呼ぶ - Promise がまだ解決してない → React が「サスペンド」判定
- いちばん近い
<Suspense>がfallback(「ワンちゃんを探してます… ⏳」)を表示 - Promise が終わったら再レンダリングされて、ワンちゃん画像が表示される
ブラウザで見ると、最初だけ「読み込み中…」がチラッと出て、その後に画像が出てくるはずです 🥰
5. use(Promise) を使うときのルールまとめ 📏
ちょっとだけ真面目な話もしましょう。
✅ やっていいこと
-
コンポーネント or カスタムフックの中で
use(promise)を呼ぶ -
Promise は「外」で作る or ちゃんとキャッシュする
- モジュールスコープの
const promise = ... - もしくは、
MapやReact.cacheで「同じ引数なら同じ Promise」を返す (Qiita)
- モジュールスコープの
-
<Suspense>で包んでfallbackを用意する
❌ やっちゃダメなこと
-
レンダーのたびに新しい Promise を作って、そのまま
useに渡す// ❌ これは NG 例(毎回 new Promise になっちゃう)
function BadComponent() {
const data = use(fetch("...").then((r) => r.json()));
// ...
}こういう書き方をすると、React 19 のブログにもあるとおり、 「キャッシュされてない Promise をサスペンドさせてるよ!」という警告が出ます。(react.dev)
-
イベントハンドラの中で
useを呼ぶ// ❌ これも NG
<button
onClick={() => {
const data = use(somePromise); // ← これはダメ
}}
>
クリック
</button>useは「レンダー中にしか呼んじゃダメな API」です。useStateやuseEffectと同じで、「クリックされたあとに使う」のは想定されていません。(Medium) -
try-catchの中からuseを呼ぶ エラー処理は「エラーバウンダリ」という仕組みでやるのが基本スタイルです(118 章でやります)。(react.dev)
6. ちょっとだけ Q&A コーナー 💌
Q1. async/await と何がちがうの?
async/awaitは「普通の関数」で Promise を待つ文法use(Promise)は「React のレンダリングの中で Promise を待つための仕組み」
use を使うと、「待っている間の画面」も React が自動でコントロールしてくれる ところがいちばん大きな違いです ✨
Q2. じゃあ useEffect はもういらないの?
いえいえ、まだまだ現役です 🧓✨
useEffectは「DOM を直接さわる」「イベントリスナーを貼る」「外部ライブラリと同期する」みたいな “副作用” 用use(Promise)は「データを取ってきて表示する」みたいな “データ取得” 用
という感じで、役割がちょっと違います。
この章では「データ取得なら use(Promise) も選択肢だよ〜」と覚えてくれれば OK 🙆♀️
Q3. ふつうの Vite + SPA でも use(Promise) 使っていいの?
- React 公式としては、「Promise はちゃんとキャッシュしてね」「
<Suspense>と一緒に使ってね」という前提つきで OK、というスタンスです。(react.dev) - Next.js などのフレームワークでは「Server Components で Promise を作って、それを Client Component に渡して
useする」スタイルが主流になりつつあります。(react.dev)
この教材ではまず Vite のシンプルな SPA で「動きのイメージ」をつかむ → そのあとで「フレームワークだとどう活かせるか」を見ていく、という流れにしていきます 🌈
7. ミニ練習問題 ✍️(やってみたらきっと身につく)
時間があるときに、VS Code で実際に手を動かしてみてください!
練習 1:ユーザーカードを use(Promise) で作る 👤
-
https://jsonplaceholder.typicode.com/users/1からユーザー情報を取ってくる -
UserApiResponse型を自分で定義する(name,emailあたりだけでOK) -
この章のワンちゃん例と同じように、
userApi.tsで Promise を 1 個だけ作るUserCard.tsxでconst user = use(userPromise);として表示App.tsxの<Suspense>の中で<UserCard />を表示
👉 ポイント:型をちゃんと書いて、user.name にマウスを載せて型情報が見えるかチェックしてみてね 👀
練習 2:同じ Promise を 2 つのコンポーネントで共有してみる 👫
UserName.tsx:ユーザー名だけ表示するコンポーネントUserEmail.tsx:メールアドレスだけ表示するコンポーネントApp.tsxで同じuserPromiseを両方のコンポーネントに渡す
<Suspense fallback={<p>ユーザー情報を読み込み中…</p>}>
<UserName userPromise={userPromise} />
<UserEmail userPromise={userPromise} />
</Suspense>
👉 ちゃんと 同じ Promise を共有できていれば、ネットワークタブで「1 回だけリクエストが飛んでいる」 はずです(余裕があれば確認してみてね)🐾
8. まとめ 🌸
use(Promise)は 「Promise の中身を 1 行で取り出せる」 React v19 の新 API- 読み込み中は
<Suspense>におまかせ、手動のloadingフラグいらず ✨ - Promise は「コンポーネントの外で作る or ちゃんとキャッシュする」のが超だいじ
- イメージがつかめたら、次の章で 「サスペンドって何?」「Suspense ってどう使うの?」 をもう少し深掘りしていきます 🚀
ここまで来たら、あなたはもう「React v19 世代のデータ取得」をちゃんと一歩リードしてますよ〜 👏💕