はじめに
React19がリリースされました。React19ではform
要素に関わる変更が多くありました。
react
ではuseActionState
、useOptimistic
APIの追加、react-dom
ではHTMLのform
要素が組み込みコンポーネントとして動作するようになり、useFormStatus
APIの追加が行われました。
これらのAPIを組み合わせるとどのような世界が見えてくるのでしょうか。この記事ではこれらのAPIの説明と組み合わせて利用した姿を紹介します。
form
form
はreact-dom
によって標準のform
を強化されたコンポーネントとなっています。
後に紹介するuseFormStatus
のサポートの追加や、action
にURL以外の関数を渡せるようになりました。
直感的な使い心地は変わりませんが、action
の使い方は標準から外れていること、react-dom
のAPIのためにパッチが当てられていることは頭の片隅に置いておくと良いかもしれません。
useActionState
useActionState
は状態とそれに対する操作をセットで扱うAPIです。
引数として操作と初期値を受け取って、状態と操作と操作が完了するまでの待機状態を返します。
// 同期的なaction(isPendingは常にfalse)
const [count, action] = useActionState(
(prevCount, formData) => {
const incrementCount = Number(formData.get('count') ?? 0);
return prevCount + incrementCount;
},
0,
);
// 非同期的なaction
const [count, action, isPending] = useActionState(
async (prevCount, formData) => {
const incrementCount = Number(formData.get('count') ?? 0);
await updateCount(prevCount + incrementCount);
return prevCount + incrementCount;
},
0,
);
useActionState
は任意で第3の引数を持ちます。第3引数には操作後に遷移するURLを渡します。
JavaScriptが完全に読み込まれたときは何の効果もない引数ですが、操作が完了してもJavaScriptが読み込まれる前の段階であれは第3引数のURLに遷移します(JavaScriptの伴う操作の場合も効果はないです)。
useOptimistic
useOptimistic
は状態の楽観的更新を行うためのAPIです。
引数として楽観的更新を行う状態とそれを楽観的に更新する関数を受け取って、楽観的な更新を行った後の状態と楽観的な更新を行う関数を返します。
// optimisticUserは楽観的更新を行った状態
const [optimisticUser, addOptimistic] = useOptimistic(
// 元となる状態
user,
// 楽観的更新をどのように行うかの関数
// addOptimisticはuserが挿入された関数
(prevUser, name) => {
return {
...prevUser,
name,
};
},
);
useFormStatus
useFormStatus
はreact-domに追加された、直近のform
のステータスを返すAPIです。
返す値は、form
の待機状態、form
の送信するデータ、form
の送信するHTTPメソッド、form
に登録した操作を返します。
const { pending, data, method, action } = useFormStatus();
組み合わせる
これまで紹介してきたAPIを組み合わせて簡単にユーザー情報を編集するフォームを作ります。
それぞれが綺麗に噛み合って、楽観的な更新等のリッチな機能かつ完結なコードになったかと思います。React18以前で、同様の実装を行うとより恩恵を感じやすいと思うのでぜひ試してみてください。
おわりに
React19でフォーム周りのサポートが充実したことで、フォーム周りのコードが完結に扱いやすくなりました。
複雑すぎるフォームではライブラリの助けを借りたいこともありそうですが、多くのフォームではReactの標準APIで十分な対応が可能そうに感じました。
さらに、JavaScriptなしでの動作も定義しやすいようなAPI設計となっているので、これからの世界では積極的に利用していきたいです。