概要
ローディングとは「くるくる回すUI」ではない。
それは**“ユーザーが何を待っているのかを可視化し、操作の意味を伝え、非同期とUIの整合性を保証する制御設計”**である。
非同期処理においてローディングの設計が曖昧だと、多重リクエスト・無反応・誤認識などが発生し、UXを損なう原因になる。
正しいローディング設計は、非同期制御 + 状態管理 + UI制御の接点として構築される必要がある。
1. ローディング設計の3原則
① 状態管理:何に対して待機しているかを識別可能に
② UI反映:表示されているUIに対して明示的に可視化
③ 制御連携:非同期の開始 / 成功 / 失敗 / 中断 との整合性
2. 状態単位のローディング制御
const [isLoading, setLoading] = useState(false);
async function handleSubmit() {
setLoading(true);
try {
await api.submitForm(form);
} finally {
setLoading(false);
}
}
- ✅ 非同期と同期してローディングが開始 / 終了する
- ✅ try-finally を徹底し、失敗やキャンセルでも確実に終了
3. コンテキストごとの粒度設計
- ボタン単位:ボタン操作に限定したローディング
- セクション単位:リスト・フォームなど局所的待機
- ページ全体:ページ切替・初期取得など全体反映
const loadingState = reactive({
createUser: false,
fetchList: false,
});
- ✅ ローディングは一つではなく、意味単位に分離すべき
4. UI操作制御との連携(ボタン制御)
<button disabled={isLoading}>
{isLoading ? '送信中...' : '送信'}
</button>
- ✅ 待機中の多重クリック防止
- ✅ ボタンやUI部品の状態変化を通じて体験としての一貫性を担保
5. キャンセルとの整合性(AbortController 連携)
const controller = new AbortController();
setLoading(true);
try {
await fetch('/api', { signal: controller.signal });
} catch (e) {
if (e.name === 'AbortError') console.log('キャンセル');
} finally {
setLoading(false);
}
- ✅ ローディングはキャンセル時も必ず終了
- ✅ 中断されたUIを表示するか、スルーするかを設計する
6. UXとしての“待ち方”を設計する
- ページ初回 → スケルトン / プレースホルダ表示
- 操作中 → ボタン/フォームUIの変化
- 非同期長時間 → ステップバー・残り時間表示
- ✅ 視覚的に待っていることを伝えるだけでなく
- ✅ 何を待っているかを明確に設計することがUXに直結
設計判断フロー
① どの処理に対してローディングが発生するか? → 意味単位で定義されているか?
② UIに明示的にローディング状態が反映されているか?
③ 非同期が失敗・中断した場合もローディング終了しているか?
④ UIコンポーネントの状態制御とローディングが連動しているか?
⑤ “なぜ待っているのか”がユーザーに伝わる設計になっているか?
よくあるミスと対策
❌ fetch の外に setLoading(false) を置いて終了しないことがある
→ ✅ try-finally で常にローディング終了を保証
❌ 1つの isLoading に全ての処理を集約して状態が破綻
→ ✅ 機能単位にローディング状態を分離管理
❌ ローディングは表示されるが、ユーザーは何が起きているか分からない
→ ✅ “何を待っているか”を文言と構造で明示
結語
ローディングとは「読み込み中を表示すること」ではない。
それは**“処理の整合性を保証し、ユーザーに待機を伝え、誤操作を防ぎ、安心を設計するUIと非同期の接点”**である。
- 非同期と同期した明示的な状態管理を行い
- UIに応じて粒度ごとにローディングを分離し
- 状態変化・キャンセル・失敗にも確実に対応し
- UXとして「待ち方」までを含めて設計する
JavaScriptにおけるローディング設計戦略とは、
“非同期の不確実性を制御し、ユーザーに安心と一貫性を届けるためのUX構造”である。