🦁
ReactAriaの<Form>コンポーネントとuseActionStateを使う例
最近はReactAriaComponentを活用している@zaruです、こんにちは。
Next.js v15(React19)で、ReactAriaの <Form>
コンポーネントと useActionState
を使っていると、Next.js v15からはフォーム内容がリセットされるの影響で期待した動作にならないことがあります。具体的には useActionState
を使って入力値を defaultValue
で保持しても、リセットされてしまう現象です。
この記事では、その対処法をメモしておきます。ステートのデータ構造は例なので参考程度に。
"use client";
import { changeName } from "@/app/action";
import { type FormEvent, startTransition, useActionState } from "react";
import {
Button,
FieldError,
Form,
Input,
Label,
TextField,
} from "react-aria-components";
export default function Home() {
const [state, formAction] = useActionState(changeName, {
payload: { username: "zaru" },
});
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
startTransition(() => formAction(formData));
};
return (
<Form validationErrors={state.errors} onSubmit={handleSubmit}>
<TextField name="username" defaultValue={state.payload.username}>
<Label>Username</Label>
<Input />
<FieldError />
</TextField>
<Button type="submit">Submit</Button>
</Form>
);
}
"use server";
export interface Payload {
username: string;
}
export interface ResponseType {
payload: Payload;
errors?: Partial<Record<keyof Payload, string>>;
}
export async function changeName(
prevState: ResponseType,
formData: FormData,
): Promise<ResponseType> {
const username = formData.get("username") as string;
return {
payload: { username },
errors: {
username: username.length > 5 ? "長すぎます" : "",
},
};
}
ポイントは以下の2点です。
-
action
ではなくonSubmit
で実行させる -
useActionState
のアクションはstartTransition
の中で実行する
これで煩雑なフォームの状態管理をuseActionStateに任せつつ、ReactAriaComponentの <Form>
のエラーメッセージ表示など便利な機能を使うことができます。
リセットもしたい場合
上記の例だとステートが維持されるため入力内容はそのままです。ただ、時にはフォームを完全にリセットさせたいときもあります。その場合は requestFormReset()
で手動リセットすることができます。
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
startTransition(() => {
requestFormReset(event.currentTarget); // formオブジェクトを渡しリセット
formAction(formData);
});
};
これでフォームリセットできます。ただこの実装だと常にリセットしてしまい、バリデーションエラー時に困った挙動になるため条件分岐などが必要になる点に注意してください。
Discussion