useTransition 完全入門【React】UIをブロックしない状態更新
ReactのuseTransitionとは何か、優先度の低い状態更新をバックグラウンドで処理する仕組みを初心者向けに解説。isPendingを使ったローディング表示の実装例も紹介します。
検索ボックスに文字を入力したとき、画面が固まって入力がもたつく——そんな経験をしたことはありませんか?
重いリストのフィルタリングや大量のデータ描画が原因で、UI が応答しなくなることがあります。useTransition を使うと、緊急でない処理をバックグラウンドに回し、UIの応答性を維持できます。
この記事でわかること:
useTransitionが必要な場面isPendingとstartTransitionの使い方- タブ切り替えや検索フィルタリングでの実装例
- 使えない場面(入力フィールドのstate)
useTransition とは?
useTransition は、状態更新の優先度を下げて、UIをブロックせずに処理するフックです。
const [isPending, startTransition] = useTransition();| 戻り値 | 説明 |
|---|---|
isPending | トランジション処理が進行中かどうかを示す boolean |
startTransition | 処理をトランジション(低優先度)としてマークする関数 |
動作の仕組み
通常の状態更新:
入力 → 状態更新 → 重い再レンダリング → UI が固まる → 次の入力を受け付ける
useTransition を使った更新:
入力 → 通常の状態更新(即時)
→ startTransition の状態更新(バックグラウンドで処理)
→ ユーザーは操作を続けられる
基本的な使い方
import { useState, useTransition } from "react";
function SearchPage() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
setQuery(value); // ← 入力は即時反映(高優先度)
startTransition(() => {
// ← 検索結果の更新は低優先度(バックグラウンド)
setResults(filterItems(value));
});
};
return (
<>
<input value={query} onChange={handleChange} placeholder="検索..." />
{isPending ? (
<p>検索中...</p>
) : (
<ul>
{results.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
)}
</>
);
}実用例:タブ切り替えのUX改善
重いコンテンツを持つタブを切り替えるときに useTransition が役立ちます。
import { useState, useTransition } from "react";
function TabContainer() {
const [tab, setTab] = useState("home");
const [isPending, startTransition] = useTransition();
const switchTab = (newTab) => {
startTransition(() => {
setTab(newTab); // タブの切り替えを低優先度で処理
});
};
return (
<div>
<nav>
{["home", "about", "posts"].map((t) => (
<button
key={t}
onClick={() => switchTab(t)}
style={{ opacity: isPending ? 0.6 : 1 }}
>
{t}
</button>
))}
</nav>
{/* isPending 中は現在のタブを薄く表示し続ける */}
<div style={{ opacity: isPending ? 0.5 : 1 }}>
<TabContent tab={tab} />
</div>
</div>
);
}useTransition を使わない場合、新しいタブのコンテンツが描画されるまで画面が固まります。useTransition を使うと、処理中も現在のタブを表示し続け、isPending で「切り替え中」の視覚的フィードバックを提供できます。
使えない場面:入力の制御
入力フィールドの state には startTransition を使えません。
// ❌ NG:入力値の state はトランジションにできない
const handleChange = (e) => {
startTransition(() => {
setInputValue(e.target.value); // これは動作しない
});
};
// ✅ OK:入力値は即時更新、表示用のデータをトランジションにする
const handleChange = (e) => {
setInputValue(e.target.value); // 即時更新
startTransition(() => {
setDisplayData(processData(e.target.value)); // 低優先度
});
};useTransition と useDeferredValue の使い分け
| フック | 対象 | 使う場面 |
|---|---|---|
useTransition | 状態更新のコード | 自分でイベントハンドラを書ける場合 |
useDeferredValue | 受け取った値 | props や外部からの値を遅延させたい場合 |
注意点
startTransitionに渡す関数は同期的に実行されるawaitの後の状態更新は別のstartTransitionでラップが必要- トランジション中にユーザーが別の操作をすると、処理は中断される
まとめ
useTransitionは状態更新の優先度を下げてUIの応答性を維持するフックisPendingでトランジション中のローディング表示ができる- 重いリスト描画・タブ切り替えなどに効果的
- 入力フィールドの state には使えない
- props を受け取る側は
useDeferredValueが適している
PR
useTransition 公式ドキュメントで詳細を確認しよう
useTransitionのインタラクティブなサンプルや、Suspenseとの組み合わせ例は公式ドキュメントで確認できます。
useTransition 公式ドキュメントを見る →