React 19 の便利そうな新しい Hook を探ってみた
次のメジャーバージョンである React 19 では、 React Server Components と Next.js だけ注力されるではなく、新しいクライアントサイドフックが導入されます。
React 19の新しいフックは、データフェッチとフォーム処理という2つの主要な課題を注目することで、SPA の開発者を含む全ての React 開発者の生産性を向上させるでよう。
それでは早速、新しいフックを見ていきましょう!
use
use
は、Promise
や Context
などの値を読み込むことができる、React 19 の実験的なフックです。他のフックと異なり、ループや条件分岐の中で呼び出すことができる最大な特徴です。他のフックと同じく、React コンポーネント内か他のフック(Custom Hook)内にだけ使用できます。
use(Promise)
と use(Context)
をそれぞれ見てみましょう。
use(Promise)
この新しいフックは、クライアント側で「suspending」(コンポーネントのレンダリングを一時停止)するためのAPIです。Promise
オブジェクトを渡すことができ、ReactはそのPromise
が解決されるまでsuspending状態になります。React use ドキュメントによる基本的な構文はこんな感じ:
import { use } from 'react';
function MessageComponent({ messagePromise }) {
const message = use(messagePromise);
// ...
}
このフックはデータフェッチに使用できるのが良いですね!
マウントする時とボタンをクリックする時にデータをフェッチするの例はこんな感じ(useEffect
を使っていません):
ドキュメントにこんな警告がありましたが、React 19からはフレームワークの制限がなくなるでしょう。
Suspense-enabled data fetching without the use of an opinionated framework is not yet supported.
新しいuse
はループや条件分岐の中で呼び出すことができるので、SWR や TanStack Query などのライブラリを使わなくても簡単にデータフェッチを行えます!(もちろんこれらのライブラリはPromiseをハンドリングするだけでなく、キャッシュなどほかの機能もあります)
これから REST や GraphQL APIsを用いてSPAを実装するのがもっと簡単になりそうですね!
use(Promise)フックの詳細はドキュメントをご参考ください。
use(Context)
use フックを使ってReact Contextを読み取ることもできます。ループやif文の中でも呼び出せるという点を除けば、useContext とまったく同じです。
import { use } from 'react';
function HorizontalRule({ show }) {
if (show) {
const theme = use(ThemeContext);
return <hr className={theme} />;
}
return false;
}
今までループや条件文の中で context を読み取りたいときはコンポーネントを分割しないといけないですが、新しい use(Context) フックを使えばコンポーネントの階層が簡素化されます。
また、パフォーマンスの面でも大きな進化です。contextが変更された場合でも、条件付きでコンポーネントの再レンダリングをスキップできるようになりました。
use(Context)フックの詳細はドキュメントをご参考ください。
Form Actions
Form Actions
は、関数を <form> の action 属性に渡すことができます。フォームが送信されると、Reactがその関数を呼び出します。
<form action={handleSubmit} />
React 18 で↑のようなコードを書くとこんな警告が出てくると思います:
Warning: Invalid value for prop action on <form> tag. Either remove it from the element or pass a string or number value to keep it in the DOM.
React 19 ではこんな感じにフォームを書けられます:
↑のaddToCart
関数はサーバーアクションではなく、クライアント側で呼び出されています。しかも非同期関数にすることもできます。
これにより、React での ajax フォームの扱いがかなり簡素化されます。しかし、やはり単に Promise を処理するだけでなく、バリデーションなど多くの機能を持つReact Hook Form
のようなライブラリを完全に捨てることができないと思います。
また、上の例にはいくつかのユーザビリティの問題があるかもしれません(送信中にボタンが無効化されない、確認メッセージがない、カートの更新が遅いなどなど)。幸いなことに、このユースケースを助ける新しいフックが追加される予定です。
<form action>
の詳細は、React ドキュメントをご参考ください。
useFormState
useFormState
は、上記で説明した非同期の form action
機能のための新しいフックです。useFormState
を呼び出すと、最後にフォームが送信されたときの action
の戻り値にアクセスできます。
import { useFormState } from 'react-dom';
import { action } from './action';
function MyComponent() {
const [state, formAction] = useFormState(action, null);
// ...
return <form action={formAction}>{/* ... */}</form>;
}
これによって、form action
から返されたメッセージやエラーを表示することができます。
useFormState
の詳細はReact ドキュメントをご参考ください。
useFormStatus
useFormStatus
は、親の<form>
が送信状態や送信データを取得できます。form の子要素から呼び出すことができ、以下のようなプロパティを持つオブジェクトを返します。
const { pending, data, method, action } = useFormStatus();
data
プロパティを使って、ユーザーが送信しようとしているデータを表示することができます。また、次の例のように、フォームが送信中の間はボタンを無効化するなどして、ペンディング状態を表示することもできます。
useFormState
とあわせて、このフックはクライアント側のフォームの UX を、無駄なコンテキストやエフェクトでコンポーネントを汚すことなく向上させます。
useFormStatus
フックの詳細は、React ドキュメントをご覧ください。
useOptimistic
この新しいフックを使うと、action
が送信されている間に楽観的に(actionが成功に送信される想定)UIを更新できます。
import { useOptimistic } from 'react';
function AppContainer() {
const [optimisticState, addOptimistic] = useOptimistic(
state,
// updateFn
(currentState, optimisticValue) => {
// merge and return new state
// with optimistic value
},
);
}
上のカートの例では、このフックを使って、Ajax呼び出しが終了する前に新しい商品が追加されたカートの状態を表示できます。
UIを楽観的に更新することは、Web アプリの UX を大きく改善する方法の一つです。このフックはこのユースケースに対して役に立ちます。
useOptimistic
フックの詳細は、React ドキュメントをご覧ください。
まとめ
これらの機能はすべて、Next や Remix のような SSR フレームワークが必要なく、Viteでバンドルされたクライアントだけの React アプリで動作できます。もちろん、SSR のReactアプリでも使用できます。
これらの機能により、Reactにおけるデータフェッチとフォームの実装が結構に簡単になります。
しかし、良い体験を提供するには、これらのフックを統合する必要があり、複雑になる可能性があります。(代替案として、楽観的な更新が組み込まれたreact-admin
のようなフレームワークを使うこともできます。)
これらの機能がReactの安定版でリリースされるのを心待ちにしています!
Discussion