React 19 では、データ更新とそれに伴う一連の流れをうまく扱う方法として Actions が導入されるようです。 例えば、これまでフォームの送信中の状態を次のように扱うことがありましたが、これが React の機能として標準化されます。
const [isPending, setIsPending] = useState(false);
const submit = () => {
setIsPending(true);
await foo();
setIsPending(false);
}
return (
<form>
<button click={submit} disable={isPending}>foo</button>
</form>
)
新しく導入される Actions の一つである useActionState
を利用して書くと次のように書けます。
const [error, submitAction, isPending] = useActionState(
async () => {
const error = await foo();
if (error) { return error; }
return null;
}
)
return (
<form action={submitAction}>
<button type="submit" disable={isPending}>foo</button>
</form>
)
Hook に渡した関数は実質的に submitAction
の内容になっていて、isPending
はその関数の実行状態に合わせて値が変化します。
これによって最初のコードと同等に isPending
を使って UI の状態を変更できる、ということのようです。
これに組み合わせて使えるものとして useFormStatus
という親の form
の実行状態を取得することが出来る Hook もあります、これは react-dom から提供されるものです。
例えばボタンコンポーネントを作るときにその状態を外から Props として渡すことがあると思いますが、 Actions を使った form の中ではこれを簡略化することが出来ます。
function FooSubmit() {
const { pending } = useFormStatus();
<button type="submit" disable={pending}>foo</button>
}
function FooForm() {
const [error, submitAction, isPending] = useActionState(
async () => {
const error = await foo();
if (error) { return error; }
return null;
}
)
return (
<form action={submitAction}>
<FooSubmit />
</form>
)
}
これが useActionState
の例と同じ動作になるようです。
Actions が失敗したときに巻き戻される useState
のようなものです。次にコードで await foo()
が失敗すると、 value
が currentValue
に戻ります。
const [value, setValue] = useOptimistic('currentValue')
const [error, submitAction, isPending] = useActionState(
async () => {
setValue('newValue')
const error = await foo();
if (error) { return error; }
return null;
}
)>
ユーザー名を変更したいが、重複チェックに時間がかかるような場合に、結果の予定を先に UI に反映しておくような場合にその実装が標準化できるようです。