React 19の新しい`use` APIとPromise処理の変化

React 19では新フック`use`が導入され、レンダー中にPromiseやContextなどの「リソース」を直接読み取れるようになりました ([What’s new in React 19 - Vercel](https://vercel.com/blog/whats-new-in-react-19#:~:text=The%20%60use%60%20function%20offers%20first,by%20the%20nearest%20Suspense%20boundary)) ([What’s new in React 19 - Vercel](https://vercel.com/blog/whats-new-in-react-19#:~:text=function%20Cart%28))。これにより**コンポーネントのレンダリング中にPromiseの結果を待つ（サスペンドする）ことが可能**になり、従来よりも簡潔に非同期データを扱えます。例えば、サーバーまたは他の場所で取得したPromiseをコンポーネントに渡し、コンポーネント内で`use(promise)`と呼ぶと、ReactはそのPromiseが解決するまでコンポーネントのレンダリングを一時停止します ([React v19 – React](https://react.dev/blog/2024/12/05/react-19#:~:text=function%20Comments%28)) ([React v19 – React](https://react.dev/blog/2024/12/05/react-19#:~:text=function%20Page%28))。Promiseが解決済みであれば即座に値を返し、未解決であれば内部的にそのPromiseを`throw`して最近接の`<Suspense>`にキャッチさせます ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=,rejects%2C%20it%20throws%20an%20error)) ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=Under%20the%20covers%2C%20in%20that,fallback%20until%20the%20Promise%20resolves))（エラーならErrorとして`throw`され、エラーバウンダリで捕捉）。Reactはこの`throw`されたThenable（Promise）を検知して該当コンポーネントを「サスペンド中」とマークし、親の`<Suspense>`はフォールバックUI（ローディング表示など）をレンダリングします ([React v19 – React](https://react.dev/blog/2024/12/05/react-19#:~:text=return%20)) ([use – React](https://react.dev/reference/react/use#:~:text=Because%20,will%20replace%20the%20Suspense%20fallback))。Promiseが解決するとReactはサスペンドしていたコンポーネントを再レンダリングし、`use`は解決結果の値を返すためコンポーネントは本来のUIを表示できます ([use – React](https://react.dev/reference/react/use#:~:text=Because%20,will%20replace%20the%20Suspense%20fallback)) ([use – React](https://react.dev/reference/react/use#:~:text=When%20passing%20a%20Promise%20from,value%20of%20such%20a%20Promise))。この仕組みにより、**Reactはレンダリングの途中で一時停止・再開が可能となり**（React 18以前はレンダリング中に同期的に処理するしかなく、データ待ちには副作用フックや外部状態管理が必要でした ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=The%20introduction%20of%20,quite%20a%20lot%20of%20boilerplate))）、非同期処理のコードが大幅に簡潔になりました。

`use`フックの特筆すべき点は**レンダリング中のPromise待機を公式にサポートした**ことです。従来はSuspenseを使ったデータ取得に特殊なパターン（例：レンダリング中にPromiseを`throw`するようなライブラリ）や事前データ取得が必要でしたが、React 19では`use`にPromise（Thenable）を渡すだけで同等のことが行えます ([React v19 – React](https://react.dev/blog/2024/12/05/react-19#:~:text=%2F%2F%20,the%20promise%20resolves)) ([What’s new in React 19 - Vercel](https://vercel.com/blog/whats-new-in-react-19#:~:text=The%20%60use%60%20function%20offers%20first,by%20the%20nearest%20Suspense%20boundary))。さらに`use`は他のHookと異なり**条件分岐やループの中でも呼び出し可能**です ([What’s new in React 19 - Vercel](https://vercel.com/blog/whats-new-in-react-19#:~:text=The%20%60use%60%20function%20offers%20first,by%20the%20nearest%20Suspense%20boundary))（呼び出し順序に縛られず、必要なときにだけ実行される）。これは`use`がコンポーネントの状態を持つ通常のHookというより、「リソースを読む」ための特殊APIで、内部でPromiseやContextの参照状況を管理するためです。そのため`use`にはいくつか制約があり、**新たに生成されたPromiseをそのまま渡す場合は適切にキャッシュ**する必要があります ([React v19 – React](https://react.dev/blog/2024/12/05/react-19#:~:text=,promises%20created%20in%20render))。例えばクライアントコンポーネント内で毎回新しいPromiseを作って`use`すると毎レンダリングで異なるPromiseを投げ続け無限ループになるため、Reactは「レンダー中に生成された未キャッシュのPromise」に対して警告を出します ([React v19 – React](https://react.dev/blog/2024/12/05/react-19#:~:text=,promises%20created%20in%20render))。この問題への対処として、React 19では`React.cache`といったAPIが追加され、関数の戻り値（Promise）をキャッシュして安定化させる仕組みも提供されています。Next.jsなどのフレームワークやSWRなどのライブラリも内部でPromiseのキャッシュ戦略を採用し、この制約を回避しています ([React v19 – React](https://react.dev/blog/2024/12/05/react-19#:~:text=A%20component%20was%20suspended%20by,compatible%20library%20or%20framework)) ([Upgrading: Single-Page Apps | Next.js](https://nextjs.org/docs/app/building-your-application/upgrading/single-page-applications#:~:text=SWR%20is%20a%20popular%20React,library%20for%20data%20fetching))。

**Next.jsにおけるReact 19の統合と活用**  
Next.js（特にApp Routerを用いる13以降のバージョン）は、React 19のこれら新機能を深く統合しています。Next.jsはReact Server Components (RSC)を採用しており、サーバー上で実行されるコンポーネントとクライアント上で実行されるコンポーネントを組み合わせてページを構築します。React 19の`use`フックやSuspenseを活用することで、**データフェッチをサーバーで開始しつつレンダリングをブロックしない**というパターンが可能になりました。具体的には、**サーバーコンポーネントからクライアントコンポーネントへPromiseをプロップ経由で渡し、クライアント側でそのPromiseを`use`で消費する**という手法です ([use – React](https://react.dev/reference/react/use#:~:text=The%20Client%20Component%20then%20takes,created%20by%20the%20Server%20Component)) ([use – React](https://react.dev/reference/react/use#:~:text=export%20function%20Message%28,))。Next.jsのAppディレクトリでは以下のようなコードパターンが推奨されています。例えばレイアウト（サーバーコンポーネント）でユーザーデータを取得する際、`await`で待たずにまずPromiseを得てしまい、それをコンテキストプロバイダなどのクライアントコンポーネントに渡します ([Upgrading: Single-Page Apps | Next.js](https://nextjs.org/docs/app/building-your-application/upgrading/single-page-applications#:~:text=children%2C%20%7D%3A%20,%2F%2F%20do%20NOT%20await)) ([Upgrading: Single-Page Apps | Next.js](https://nextjs.org/docs/app/building-your-application/upgrading/single-page-applications#:~:text=While%20you%20can%20defer%20and,with%20a%20custom%20React%20hook)):

```tsx
// app/layout.tsx – サーバーコンポーネント
export default function RootLayout({ children }) {
  const userPromise = getUser(); // awaitせずにPromise取得
  return (
    <html>
      <body>
        {/* クライアント側のUserProviderにPromiseを渡す */}
        <UserProvider userPromise={userPromise}>
          {children}
        </UserProvider>
      </body>
    </html>
  );
}
```

そして、その受け取り側である`UserProvider`コンポーネント（クライアントコンポーネント）では`use`フックを使ってPromiseから値を読み出し、Context経由で子孫のクライアントコンポーネントに提供します。

```tsx
// app/user-provider.tsx – クライアントコンポーネント
"use client";
import { createContext, use } from 'react';
const UserContext = createContext(null);
export function UserProvider({ children, userPromise }) {
  const user = use(userPromise);  // Promiseが解決するまでサスペンド
  return <UserContext.Provider value={user}>{children}</UserContext.Provider>;
}
export function useUser() {
  const context = use(UserContext); // Contextもuseで取得可能
  if (context === null) throw new Error("useUser must be within UserProvider");
  return context;
}
```

こうしておけば、任意の深さのクライアントコンポーネントで`useUser()`を呼び出してユーザーデータを参照できます。上記の例では、`UserProvider`内で`use(userPromise)`を実行した時点で、もし`userPromise`がまだ解決していなければこのコンポーネントはサスペンド状態になります。Reactはこれを検知して`UserProvider`直上の`Suspense`境界にフォールバックUIを表示します。Next.jsではレイアウトやページで`Suspense`を利用するか、または`loading.tsx`を用意することでフォールバックを定義できます。上の例ではレイアウト内で明示的に`Suspense`を使っていませんが、Next.jsは**クライアントコンポーネントのサスペンスによる「部分的なハイドレーション」**を自動で処理します。実際、Next.js公式ドキュメントでも「Promiseを消費するコンポーネント（上記例では`Profile`など）はサスペンドされ、これによって部分的なHydrationが可能になる。JavaScriptのロードが終わる前にストリーム配信されたプリレンダリングHTMLを確認できる」と説明されています ([Upgrading: Single-Page Apps | Next.js](https://nextjs.org/docs/app/building-your-application/upgrading/single-page-applications#:~:text=))。つまり、Promiseの解決を待たずにまず**サーバーが持つ他の部分のHTMLをストリーミングレンダリングし、クライアント側では未解決データの部分だけHydrationを遅らせる**ことで全体の表示を迅速に行うのです。

**Server ComponentからClient ComponentへPromiseを渡す内部実装（Next.jsとReactの連携）**  
サーバーコンポーネントからクライアントコンポーネントにPromiseを直接渡す仕組みは、React 19とNext.jsのストリーミング機構が連携して実現されています。その内部では次のような手順が行われています ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=As%20for%20how%20this%20actually,level%20flow%20goes%20like%20this)) ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=1,the%20Promise%20ID%20it%20generated))：

1. **サーバー側でのID付与とプレースホルダー**: サーバーがコンポーネントツリーをレンダリングする際、クライアントコンポーネントのプロパティに未解決のPromiseが渡されていると分かった時点で、React/Next.jsはそのPromiseに一意の内部IDを割り当てます ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=1,the%20Promise%20ID%20it%20generated))。そして実際のPromiseオブジェクトの代わりに、そのIDを示すプレースホルダー情報をクライアントに送ります。言い換えれば、サーバーは「ここに将来ID=Xのデータが入る」という印を埋め込んだJSONペイロードないしマーカーを生成し、クライアントコンポーネントの`props`として渡します。  

2. **サーバーからのHTMLストリーミング送信**: Next.jsはReactのストリーミングSSRを用いて、生成したHTMLを逐次クライアントに送信します。この時点で未解決のデータ部分については`<Suspense>`のフォールバックUI（ローディング表示）や空コンテナのみがHTMLに含まれます ([use – React](https://react.dev/reference/react/use#:~:text=Because%20,will%20replace%20the%20Suspense%20fallback))。重要なのは、**サーバーはレスポンスをすぐには閉じずストリームを開いたままにする**ことです ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=The%20key%20to%20all%20this,Promise%20to%20resolve))。これにより、サーバーは他の部分のHTMLを先に送りつつ、バックエンドでPromiseの解決を待ち続けることができます。Next.jsではこの「ストリーミング応答」を実現するためにNode.jsのストリームやWeb Streams APIを活用し、サーバーコンポーネントの結果を逐次クライアントに流せるようになっています（React 18で導入された`suspense`対応の`renderToPipeableStream`/`renderToReadableStream`を活用）。  

3. **クライアントでの一時サスペンド**: クライアントはまず送られてきたプリレンダリングHTMLを受け取りレンダリングしますが、Promiseが未解決の部分については上記の通りフォールバックUIしかない状態です。並行して、Next.jsはサーバーから送られたもう一つの情報源（RSCペイロード）を使ってクライアントコンポーネントの初期プロップを再現します。このときプロップ内のPromiseは実体ではなく**「Promise ID」**として渡されています。Next.jsのクライアント側実装は、このIDに対応する「未解決のPromise」をクライアント上で生成または参照し、コンポーネントにその代用Promiseを与えます ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=1,the%20Promise%20ID%20it%20generated)) ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=4,the%20Promise%20ID%20it%20generated))。結果としてクライアントコンポーネント（例えば先の`Message`や`UserProvider`）は`use(promise)`を実行しますが、そのPromiseはまだ解決されていないためクライアント側でもサスペンドが発生します。クライアントではReactのHydrationプロセス中にサスペンドが起きるため、対応する`Suspense`境界（既にHTMLで描画済みのフォールバック）を維持し、コンポーネント本体の描画（およびイベントバインドなどのHydration）を一時停止します。この「部分的なHydration停止」により、ユーザーにはページ全体の骨格や他の即時表示可能な部分が先に見える一方、未解決データに依存するUI部分は読み込み中の表示のままとなります。

4. **Promise解決とデータ注入**: サーバー側でバックエンド処理が進行し、やがてPromiseが解決されると（例えばデータベースから結果を取得できた等）、サーバーは保持していたストリームを通じて**追加のHTML片として解決データを送信**します ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=The%20key%20to%20all%20this,Promise%20to%20resolve))。具体的には、先ほど割り当てたPromise IDと解決結果の値を含むスクリプトタグをHTMLストリームの末尾に挿入します ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=1,the%20Promise%20ID%20it%20generated)) ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=so%20when%20this%20%60,component))。Next.jsでは、この埋め込まれたスクリプトが実行されると内部で対応するPromiseを見つけ出し、その値でresolve（完了）させる処理が行われます ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=the%20,Promise%20was%20ID%3D9%2C%20and))。先述のようにクライアント側ではPromise IDに紐づくダミーのPromiseを持っていますから、サーバーから届いたデータでそれが完了状態になり、本物の値が得られます。  

5. **クライアントコンポーネントの再開（ハイドレーション完了）**: Promiseが解決されたことで、停止していたクライアントコンポーネントのレンダリングが再開されます。ReactはSuspenseによるサスペンドから復帰し、`use(promise)`が今回解決済みの値を返すため、クライアントコンポーネントは本来のUIを表示できます ([use – React](https://react.dev/reference/react/use#:~:text=Because%20,will%20replace%20the%20Suspense%20fallback)) ([use – React](https://react.dev/reference/react/use#:~:text=When%20passing%20a%20Promise%20from,value%20of%20such%20a%20Promise))。例えば`Message`コンポーネントなら「Here is the message: ○○」という実データ入りの内容がフォールバックと置き換わります（Context経由の場合も、コンテキスト値が更新され子コンポーネントが再レンダリングされます）。以上の一連の流れにより、サーバーからクライアントへ未解決Promiseを渡しつつ非同期データを逐次ストリーム配信することが実現されています。ポイントは**サーバー側でレスポンスを保持したまま追加データを後送りできるストリーミング機構**と、**クライアント側でPromiseの境界をSuspense/水際でハンドリングするHydration機構**です。React 19＋Next.jsはこれらを密接に連携させ、開発者は単にPromiseを渡して`use`するだけで裏側の複雑な同期を意識せずに済むようになっています。

**React 19側の変更点（新しいレンダリング方式とデータ管理）**  
以上の機能を支えるために、React 19自体にも様々な改善が加えられました。まず大きいのは**レンダリングモデルの進化**です。`use`フックの導入により、Reactは「レンダー中に途中停止して結果待ち→再開」という従来になかった処理が可能となりました ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=The%20introduction%20of%20,quite%20a%20lot%20of%20boilerplate))。これはFiber（仮想DOM再計算の仕組み）レベルでの拡張であり、Suspenseを用いた非同期レンダリングが一段と強力になっています。React 18でもSuspenseによるサーバーストリーミングは実験的に導入されていましたが、React 19では公式に安定化し、`use`と組み合わせることで**フレームワークなしの純React環境でもPromiseを使ったデータ取得とストリーミングUI表示が可能**になりました ([use – React](https://react.dev/reference/react/use#:~:text=)) ([Mastering React 19: Exploring the Latest Features of the New React ...](https://www.scalablepath.com/react/react-19#:~:text=Mastering%20React%2019%3A%20Exploring%20the,js%20or))（もっとも実際にはNext.jsのようなフレームワークが統合して使うケースが主流です）。またReact 19では**React Server Components (RSC)** が正式にサポートされました ([What’s new in React 19 - Vercel](https://vercel.com/blog/whats-new-in-react-19#:~:text=Server%20Components))。RSCはReact 18で提案された仕組みですが、React 19で安定版となり、Next.jsもこれを前提にしたApp Routerを提供しています。RSCの導入に伴い、Reactは「サーバーで実行されるコンポーネントツリーをシリアライズしてクライアントに送り、クライアントでそれを復元・一部実行する」という新たなレンダリング手法（通称「React Flight」プロトコル）を実装しています。今回の「Promiseを未解決のまま渡す」機能も、このRSCプロトコル上で**シリアライズ可能なデータ型としてThenable（Promise）を扱えるように拡張**された結果と言えます ([use – React](https://react.dev/reference/react/use#:~:text=Note)) ([use – React](https://react.dev/reference/react/use#:~:text=A%20Promise%20can%20be%20passed,Client%20Component%20as%20a%20prop))。具体的には先述のようにPromiseの解決値がJSONシリアライズ可能であることを前提にID割り当て・逐次送信する実装がReact/Next.jsに組み込まれました ([use – React](https://react.dev/reference/react/use#:~:text=Note))。さらにデータ管理の面では、React 19は`use`フックを円滑に機能させるため**リソースのキャッシュ戦略**も強化しています。公式には`use`で読むPromiseはキャッシュされたものを使うよう求められており ([React v19 – React](https://react.dev/blog/2024/12/05/react-19#:~:text=,promises%20created%20in%20render))、React自体も`React.cache()`というヘルパーを提供して関数の戻り値Promiseをメモ化できるようにしました。Next.js側でも`fetch`関数を自動でキャッシュ＆再利用したり（デフォルトで同じリクエストは結果を保持する） ([Upgrading: Single-Page Apps | Next.js](https://nextjs.org/docs/app/building-your-application/upgrading/single-page-applications#:~:text=SWR%20is%20a%20popular%20React,library%20for%20data%20fetching))、`use cache`ディレクティブによって特定の関数やコンポーネントの出力をキャッシュする独自拡張を用意するなど ([Directives: use cache | Next.js](https://nextjs.org/docs/app/api-reference/directives/use-cache#:~:text=The%20,client%20%20or%20%2092)) ([Directives: use cache | Next.js](https://nextjs.org/docs/app/api-reference/directives/use-cache#:~:text=%2F%2F%20Component%20level%20export%20async,))、React 19の仕組みを補完する形でデータの管理・再利用を最適化しています。  

まとめると、React 19では`use`フックの導入によって**Promiseを直接レンダーで扱いサスペンドできる新機構**が生まれ、Server ComponentsやSuspenseと組み合わせることでサーバー発のデータを非同期ストリーミングでクライアントUIに反映する道が開けました。Next.jsはこのReact 19の機能を統合し、Promiseをawaitせず渡すことでサーバーレンダリングをブロックしないデータ取得パターンを実現しています ([use – React](https://react.dev/reference/react/use#:~:text=return%20))。その内部ではPromiseにIDを振って逐次データを送り、クライアントでSuspend/Resumeさせるという高度な仕組みが動いています ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=1,the%20Promise%20ID%20it%20generated)) ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=The%20key%20to%20all%20this,Promise%20to%20resolve))。開発者はReact 19とNext.jsの提供するAPI（`use`, Suspense, Server/Clientコンポーネント区分など）を利用することで、複雑な非同期処理をシンプルかつ高効率に実装できるようになったと言えるでしょう。

**参考資料**: React公式ブログおよびドキュメント ([React v19 – React](https://react.dev/blog/2024/12/05/react-19#:~:text=function%20Comments%28)) ([use – React](https://react.dev/reference/react/use#:~:text=The%20Client%20Component%20then%20takes,created%20by%20the%20Server%20Component))、Next.js公式ドキュメント ([Upgrading: Single-Page Apps | Next.js](https://nextjs.org/docs/app/building-your-application/upgrading/single-page-applications#:~:text=))、Vercelブログ ([What’s new in React 19 - Vercel](https://vercel.com/blog/whats-new-in-react-19#:~:text=The%20%60use%60%20function%20offers%20first,by%20the%20nearest%20Suspense%20boundary))、技術記事 ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=1,the%20Promise%20ID%20it%20generated)) ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=The%20key%20to%20all%20this,Promise%20to%20resolve))などに、これらの仕組みに関する詳細な解説があります。特にEd Spencer氏のブログ記事は、Next.jsによるRSCストリーミングの内部処理（PromiseのID割当てやスクリプトによる値注入）について踏み込んだ説明を行っており参考になります ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=As%20for%20how%20this%20actually,level%20flow%20goes%20like%20this)) ([Promises across the void: Streaming data with RSC](https://edspencer.net/2024/7/12/promises-across-the-void-react-server-components#:~:text=The%20key%20to%20all%20this,Promise%20to%20resolve))。