⚛️
【React 19】useActionStateって何?
はじめに
React 19の新機能でuseActionState
というフックが新たに導入されました。
今回はuseActionState
についてuseState
と比較して解説していきます。
用語解説
- アクション: 非同期関数
- ServerActions: サーバ上で実行される非同期関数
- ハイドレーション: イベントハンドラの登録やインタラクティブな状態にすること
useActionStateとは
useActionState
は、フォームの状態管理を行うために設計されたフックです。
アクションの実行とその結果の管理を簡素化することができます。
useActionStateのメリット
- フォームが送信されたときに自動的にステートや実行状態を更新してくれる。
- アクションの実行状態(isPending)とその結果(state)を一元管理できる。
- ServerActionsと組み合わせて使うことで、ハイドレーションが完了する前にレスポンスの表示を可能にする。
useActionStateの使い方
const [state, formAction, isPending] = useActionState(action, initialState, permalink?);
引数
-
action
: フォームが送信されたときに呼び出される関数。-
currentState
(第1引数): 現在のステート -
formData
(第2引数): フォームの入力値
-
-
initialState
: stateの初期状態。 -
permalink
(省略可能): フォーム送信後にリダイレクトするユニークなページURL。
戻り値
-
state
: アクション実行後のステート。(1度も実行されてない場合はinitialStateが返る) -
formAction
: form要素のactionプロパティに渡す関数。 -
isPending
: アクションが実行中かを表すブール値。(useTransitionのisPendingと同じ)
サンプルコード
お問い合わせフォームの実装を想定して、useState
とuseActionState
のそれぞれで同じ機能のフォームを実装したサンプルコードになります。
useStateの場合
import { type FormEvent, useState } from "react"
// お問い合わせ内容を送信する関数
const submitInquiry = async (name: string, email: string, details: string): Promise<boolean> => {
// お問い合わせ内容をAPIにPOSTする (今回は3秒待機で模倣)
await new Promise(resolve => setTimeout(resolve, 3000))
// POSTの結果に応じてbooleanを返す (今回はランダムでbooleanを返す)
return Math.random() < 0.5
}
const InquiryForm = () => {
const [isPending, setIsPending] = useState<boolean>(false) // 実行状態フラグ
const [isError, setIsError] = useState<boolean>(false) // エラーフラグ
const [message, setMessage] = useState<string>("") // メッセージ
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
// submitイベントをキャンセル
e.preventDefault()
// 実行中にする
setIsPending(true)
// フォームデータを取得
const formData = new FormData(e.currentTarget)
const name = formData.get("name")?.toString() ?? ""
const email = formData.get("email")?.toString()?? ""
const details = formData.get("details")?.toString() ?? ""
// フォームデータを送信
const isSuccess = await submitInquiry(name, email, details)
// 結果に応じてステートを変更
if (isSuccess) {
setIsError(false)
setMessage("送信完了")
} else {
setIsError(true)
setMessage("送信失敗")
}
// 実行状態を解除
setIsPending(false)
}
return (
<form onSubmit={handleSubmit}>
<p>useStateで実装</p>
<label>
<div>名前</div>
<input type="text" name="name" required />
</label>
<label>
<div>メールアドレス</div>
<input type="email" name="email" required />
</label>
<label>
<div>お問い合わせ内容</div>
<textarea name="details" required />
</label>
{message && (
<p className={isError ? "text-red-500" : ""}>{message}</p>
)}
<div>
<button type="submit" disabled={isPending}>送信</button>
</div>
</form>
)
}
export default InquiryForm
useActionStateの場合
import { useActionState } from "react"
type FormState = {
isError: boolean
message: string
}
// お問い合わせ内容を送信する関数
const submitInquiry = async (name: string, email: string, details: string): Promise<boolean> => {
// お問い合わせ内容をAPIにPOSTする (今回は3秒待機で模倣)
await new Promise(resolve => setTimeout(resolve, 3000))
// POSTの結果に応じてbooleanを返す (今回はランダムでbooleanを返す)
return Math.random() < 0.5
}
const InquiryForm2 = () => {
const [formState, formAction, isPending] = useActionState<FormState, FormData>(
async (currentState, formData) => {
// フォームデータを取得
const name = formData.get("name")?.toString() ?? ""
const email = formData.get("email")?.toString() ?? ""
const details = formData.get("details")?.toString() ?? ""
// フォームデータを送信
const isSuccess = await submitInquiry(name, email, details)
// 結果に応じてformStateを返す
if (isSuccess) {
return { isError: false, message: "送信完了" }
}
return { isError: true, message: "送信失敗" }
},
{ isError: false, message: "" }, // formStateの初期値,
)
return (
<form action={formAction}>
<p>useActionStateで実装</p>
<label>
<div>名前</div>
<input type="text" name="name" required />
</label>
<label>
<div>メールアドレス</div>
<input type="email" name="email" required />
</label>
<label>
<div>お問い合わせ内容</div>
<textarea name="details" required />
</label>
{formState.message && (
<p>{formState.isError && "エラー: "}{formState.message}</p>
)}
<div>
<button type="submit" disabled={isPending}>送信</button>
</div>
</form>
)
}
export default InquiryForm2
デモ画面
最後に
useActionState
は、フォーム管理を簡素化できるとても便利なフックです。
今まで個々にuseState
で管理していたのをuseActionState
でまとめて管理できるので、コードもスッキリして見やすくなります。
ServerActionsと組み合わせることで、パフォーマンスやユーザー体験の向上もさせることができます。
みなさんもぜひ使ってみてください。
参考資料
https://ja.react.dev/reference/react/useActionState
https://dev.classmethod.jp/articles/react-19-understanding-use-action-state/
Discussion