useTransitionで送信状態を管理する
2025-12-09
a month ago
開発環境
- react 19.2.0
- react-dom 19.2.0
前提
- React 19 以降では、useTransition が 非同期関数を扱えるように拡張されました
- コンポーネント側で isLoading のような状態を自前で管理せず、外部 API 呼び出しのローディング状態を isPending に任せられます
- エラーハンドリングは useTransition では扱えないため、別途 try/catch などで行う必要があります
本題
useTransition が非同期関数対応した背景
React 18 までは、startTransition に渡す関数は同期的であることが前提でした。
React 19 では、公式に 非同期関数が許可され、外部 API の呼び出し処理を直接渡せるようになりました。
これにより、以下のメリットがあります。
- フォーム送信や GET リクエストの処理時に isLoading を自分で宣言しなくて良い
- transition の中で完結するため、ローディング状態が UI と自然に結びつく
- 既存の Suspense や並列 UI とも親和性が高い
サンプル実装:GET リクエストのローディング状態管理
以下は JSONPlaceholder API からデータを取得し、そのローディング状態を useTransition の isPending に任せる例です。
import { useState, useTransition } from 'react';
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
export default function App() {
const [post, setPost] = useState(null);
const [isPending, startTransition] = useTransition();
const fetchPost = async () => {
try {
const res = await fetch('https://jsonplaceholder.typicode.com/posts/1');
if (!res.ok) throw new Error('API Error');
const data = await res.json();
await sleep(1000); // 意図的に時間を長く
setPost(data);
} catch (e) {
console.error(e);
// エラーハンドリングは各自で実装
}
};
const handleClick = () => {
startTransition(async () => {
await fetchPost();
});
};
return (
<div className="mx-auto max-w-lg space-y-6 p-6">
<button
onClick={handleClick}
disabled={isPending}
className={`rounded px-4 py-2 font-medium text-white transition ${isPending ? 'cursor-not-allowed bg-gray-400' : 'bg-blue-600 hover:bg-blue-700'} `}
>
{isPending ? 'Loading...' : 'Fetch Post'}
</button>
{post && (
<div className="rounded border bg-white p-4 shadow">
<h3 className="mb-2 text-lg font-semibold">{post.title}</h3>
<p className="text-gray-700">{post.body}</p>
</div>
)}
</div>
);
}
実装ポイントまとめ
- startTransition(async () => { ... }) により非同期処理を素直に書ける
- isPending がローディング状態を表し、ボタン UI と自然に連動
- 自前の isLoading が不要になり、状態管理が簡潔化
- ただし エラー状態は useTransition では扱えないので、各自 try/catch や UI でハンドリングが必要
この API が役立つケース
- フォーム送信時の「送信中」状態を管理したい
- GET/POST のローディング表示を自然に行いたい
- UI をブロックせずに非同期処理を走らせたい
さいごに
useTransition の非同期対応によって、外部 API 通信を伴う UI のローディング制御が非常に簡潔になりました。
状態管理の考慮すべきポイントが減り、コンポーネントが読みやすく保守しやすくなります。
ただし、ローディング状態以外(成功・失敗・例外)の管理は別途必要である点に注意が必要です。