要約
Reactアプリケーションにおけるローディング表示の実装には、react-loadingライブラリが最適である。8種類のアニメーションから選択でき、軽量かつシンプルな実装が可能だ。本記事では、基本的なインストールからRedux連携、ユースケース別の実装パターン、カスタマイズ手法、トラブルシューティングまで、実践的な内容を網羅的に解説する。
対象読者: React初心者〜中級者、ローディング実装を検討中の開発者
検証環境: React 18.x、react-loading 2.0.3(2025年11月時点)
この記事を読むことで得られるメリット
この記事を読むことで以下のことが分かる:
- react-loadingライブラリの基本的な使い方とインストール手順
- Redux連携を含むローディングコンポーネントの実装方法
- 8種類のローディングアニメーションの特徴とカスタマイズ手法
- ユースケース別の実装パターン(API通信、ボタン送信、ページ全体、Suspense連携)
- 他のローディングライブラリ(react-spinners、react-loader-spinner)との比較と選定基準
- トラブルシューティングとパフォーマンス最適化の方法
この記事を読むのにかかる時間
約10分
環境
- MacOS Apple M4 Max Sequoia 15.1
- React 18.x
- react-loading 2.0.3
- Redux Toolkit 1.9.x(オプション)
react-loadingとは
react-loadingは、Reactアプリケーション向けの軽量なローディング表示ライブラリである。8種類の美しいアニメーションを標準搭載し、わずか数行のコードで導入できる手軽さが特徴だ。
主な特徴
- 軽量: バンドルサイズが小さく、パフォーマンスへの影響が少ない
- シンプル: 複雑な設定不要で即座に導入可能
- カスタマイズ性: 色・サイズ・アニメーション種類を柔軟に変更可能
- TypeScript対応: 型定義ファイルが提供されている
他のローディングライブラリとの比較
ライブラリ | バンドルサイズ | アニメーション数 | カスタマイズ性 | TypeScript対応 |
|---|---|---|---|---|
react-loading | 小(約15KB) | 8種類 | 高 | ✅ |
react-spinners | 中(約30KB) | 30種類以上 | 非常に高 | ✅ |
react-loader-spinner | 中(約25KB) | 15種類 | 高 | ✅ |
react-loadingを選ぶべきケース:
- シンプルで軽量なソリューションが欲しい
- 8種類のアニメーションで十分
- すぐに導入したい
他のライブラリを選ぶべきケース:
- より多様なアニメーションが必要(react-spinners)
- 特殊なデザイン要件がある(react-loader-spinner)
導入手順
ステップ1: インストール
npmまたはyarnでパッケージをインストールする。
npm install react-loading
または
yarn add react-loading
ステップ2: ローディングコンポーネントの作成
以下は、Redux連携を含むローディングコンポーネントの実装例である。
基本的な実装(Redux連携あり)
/src/components/common/Loading.jsx
import React from "react";
import ReactLoading from "react-loading";
import { useSelector } from "react-redux";
import { getIsLoading, getLoadingText } from "../../reducks/loading/selectors";
const Loading = ({ children }) => {
const selector = useSelector((state) => state);
const isLoading = getIsLoading(selector);
const loadingText = getLoadingText(selector);
if (isLoading) {
return (
<section className="flex justify-center items-center h-screen">
<div>
<ReactLoading
type="spin"
color="#ebc634"
height="100px"
width="100px"
className="mx-auto"
/>
<p className="text-center mt-3">{loadingText}</p>
</div>
</section>
);
} else {
return <>{children}</>;
}
};
export default Loading;
コードのポイント:
isLoadingがtrueの場合、ローディングコンポーネントを表示isLoadingがfalseの場合、childrenをそのまま表示- Reduxストアからローディング状態とテキストを取得
シンプルな実装(Redux不要)
Reduxを使用しない場合は、以下のようにローカルステートで実装できる。
import React, { useState } from "react";
import ReactLoading from "react-loading";
const SimpleLoading = () => {
const [isLoading, setIsLoading] = useState(false);
const handleClick = async () => {
setIsLoading(true);
try {
// API呼び出しなどの非同期処理
await fetchData();
} finally {
setIsLoading(false);
}
};
return (
<div>
<button onClick={handleClick}>データ取得</button>
{isLoading && (
<div className="flex justify-center mt-4">
<ReactLoading type="spin" color="#3b82f6" height={50} width={50} />
</div>
)}
</div>
);
};
ステップ3: エントリーポイントでラッパーする
ローディングコンポーネントでアプリ全体をラップすることで、グローバルなローディング表示を実現できる。
/src/index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import Loading from "./components/common/Loading";
if (document.getElementById("root")) {
ReactDOM.render(
<Loading>
<App />
</Loading>,
document.getElementById("root")
);
}
動作の仕組み:
<Loading>コンポーネントが<App />をchildrenとして受け取る- Reduxの
isLoadingがtrueの間、ローディング画面を表示 isLoadingがfalseになると、<App />が表示される
ローディングアイコンのカスタマイズ
プロパティ一覧
react-loadingコンポーネントで使用できるプロパティは以下の通りである。
プロパティ名 | 型 | デフォルト値 | 説明 |
|---|---|---|---|
type | String | "balls" | アニメーションの種類 |
color | String | "#ffffff" | ローディングアイコンの色 |
delay | Number | 0 | 表示開始までの遅延時間(ミリ秒) |
height | Number/String | 64 | 高さ(px) |
width | Number/String | 64 | 幅(px) |
className | String | "" | CSSクラス名 |
アニメーション種類(8種類)
react-loadingでは以下の8種類のアニメーションが利用可能である。
- blank - シンプルな空白表示
- balls - 複数のボールが跳ねるアニメーション
- bars - 縦棒が上下するアニメーション
- bubbles - 泡が浮かぶようなアニメーション
- cubes - キューブが回転するアニメーション
- cylon - 左右に動くスキャンライン
- spin - 円形のスピナー(最も一般的)
- spinningBubbles - 回転する泡のアニメーション
- spokes - 放射状のスポーク
カスタマイズ例
例1: 色とサイズの変更
<ReactLoading
type="spin"
color="#3b82f6" // 青色
height={80}
width={80}
/>
例2: 遅延表示
短時間の処理でローディングをチラつかせないため、遅延表示を設定する。
<ReactLoading
type="bars"
color="#10b981"
height={60}
width={60}
delay={300} // 300ms後に表示
/>
例3: カスタムCSSとの組み合わせ
<ReactLoading
type="spinningBubbles"
color="#ef4444"
height="100px"
width="100px"
className="custom-spinner"
/>
.custom-spinner {
filter: drop-shadow(0 0 10px rgba(239, 68, 68, 0.5));
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
ユースケース別の実装パターン
パターン1: API通信時のローディング
import React, { useState, useEffect } from "react";
import ReactLoading from "react-loading";
const DataFetcher = () => {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch("/api/data");
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setIsLoading(false);
}
};
fetchData();
}, []);
if (isLoading) {
return (
<div className="flex justify-center items-center h-64">
<ReactLoading type="spin" color="#3b82f6" height={60} width={60} />
</div>
);
}
if (error) {
return <div>エラー: {error}</div>;
}
return <div>{/* データ表示 */}</div>;
};
パターン2: ボタンクリック時のローディング
import React, { useState } from "react";
import ReactLoading from "react-loading";
const SubmitButton = () => {
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async () => {
setIsSubmitting(true);
try {
await submitForm();
alert("送信完了");
} catch (err) {
alert("送信失敗");
} finally {
setIsSubmitting(false);
}
};
return (
<button
onClick={handleSubmit}
disabled={isSubmitting}
className="flex items-center gap-2 px-4 py-2 bg-blue-500 text-white rounded"
>
{isSubmitting ? (
<>
<ReactLoading type="spin" color="#ffffff" height={20} width={20} />
送信中...
</>
) : (
"送信"
)}
</button>
);
};
パターン3: ページ全体のローディング
import React from "react";
import ReactLoading from "react-loading";
const FullPageLoading = ({ isLoading, children }) => {
if (isLoading) {
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center z-50">
<div className="bg-white p-8 rounded-lg shadow-xl">
<ReactLoading type="spinningBubbles" color="#3b82f6" height={100} width={100} className="mx-auto" />
<p className="text-center mt-4 text-gray-700">読み込み中...</p>
</div>
</div>
);
}
return <>{children}</>;
};
パターン4: React Suspenseとの組み合わせ
import React, { Suspense } from "react";
import ReactLoading from "react-loading";
const LoadingFallback = () => (
<div className="flex justify-center items-center h-screen">
<ReactLoading type="cubes" color="#8b5cf6" height={80} width={80} />
</div>
);
const App = () => {
return (
<Suspense fallback={<LoadingFallback />}>
<LazyComponent />
</Suspense>
);
};
Redux Toolkitでの状態管理
最新のRedux Toolkitを使用した実装例を紹介する。
loadingSlice.js
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
isLoading: false,
loadingText: '読み込み中...'
};
const loadingSlice = createSlice({
name: 'loading',
initialState,
reducers: {
startLoading: (state, action) => {
state.isLoading = true;
state.loadingText = action.payload || '読み込み中...';
},
stopLoading: (state) => {
state.isLoading = false;
state.loadingText = '';
}
}
});
export const { startLoading, stopLoading } = loadingSlice.actions;
export default loadingSlice.reducer;
使用例
import { useDispatch } from 'react-redux';
import { startLoading, stopLoading } from './store/loadingSlice';
const MyComponent = () => {
const dispatch = useDispatch();
const fetchData = async () => {
dispatch(startLoading('データを取得中...'));
try {
await api.fetchData();
} finally {
dispatch(stopLoading());
}
};
return <button onClick={fetchData}>データ取得</button>;
};
トラブルシューティング
問題1: ローディングが表示されない
原因:
isLoadingの状態管理が正しく機能していない- コンポーネントが正しくマウントされていない
解決策:
// Redux DevToolsで状態を確認
console.log('isLoading:', isLoading);
// コンポーネントのマウント確認
useEffect(() => {
console.log('Loading component mounted');
}, []);
問題2: ローディングが消えない
原因:
- 非同期処理のエラーハンドリング漏れ
finallyブロックでの状態更新忘れ
解決策:
const fetchData = async () => {
setIsLoading(true);
try {
await api.call();
} catch (error) {
console.error(error);
} finally {
// 必ずここで停止
setIsLoading(false);
}
};
問題3: アニメーションが動かない
原因:
- CSSが正しく読み込まれていない
- z-indexの競合
解決策:
// z-indexを明示的に指定
<div style={{ zIndex: 9999, position: 'relative' }}>
<ReactLoading type="spin" color="#3b82f6" />
</div>
よくある質問(FAQ)
Q1: React 18のStrictモードで二重レンダリングされる問題は?
A: React 18のStrictモードでは開発時に意図的に二重レンダリングが発生するが、これはreact-loadingの動作には影響しない。本番ビルドでは正常に動作する。
Q2: アニメーションの速度を変更できるか?
A: react-loadingは速度変更のプロパティを提供していないが、CSSアニメーションをオーバーライドすることで調整可能である。
.custom-spinner svg {
animation-duration: 0.5s !important; /* デフォルトより高速化 */
}
Q3: TypeScriptで型エラーが出る場合は?
A: 型定義ファイルを明示的にインポートする。
import ReactLoading, { LoadingType } from "react-loading";
const type: LoadingType = "spin";
Q4: サーバーサイドレンダリング(SSR)で使えるか?
A: react-loadingはクライアントサイド専用のため、Next.jsなどで使用する場合は動的インポートが必要である。
import dynamic from 'next/dynamic';
const ReactLoading = dynamic(() => import('react-loading'), {
ssr: false
});
Q5: アクセシビリティ対応は?
A: roleとaria-labelを追加することで対応可能である。
<div role="status" aria-label="読み込み中">
<ReactLoading type="spin" color="#3b82f6" />
<span className="sr-only">読み込み中...</span>
</div>
パフォーマンス最適化のヒント
1. 遅延表示の活用
短時間で完了する処理の場合、ローディングを一瞬表示するとチラつきが発生する。delayプロパティで300-500ms程度の遅延を設定すると良い。
<ReactLoading type="spin" color="#3b82f6" delay={300} />
2. 条件付きインポート
使用頻度が低い場合は、動的インポートでバンドルサイズを削減する。
const [ReactLoading, setReactLoading] = useState(null);
useEffect(() => {
if (needsLoading) {
import('react-loading').then((module) => {
setReactLoading(() => module.default);
});
}
}, [needsLoading]);
3. メモ化
不要な再レンダリングを防ぐため、React.memoを活用する。
const Loading = React.memo(({ isLoading, children }) => {
if (isLoading) {
return <ReactLoading type="spin" color="#3b82f6" />;
}
return <>{children}</>;
});
まとめ
本記事では、Reactのローディングライブラリ react-loading の導入手順から実践的なカスタマイズ方法まで解説した。
重要なポイント:
- react-loadingは軽量で導入が簡単なローディングライブラリ
- 8種類のアニメーションから選択可能
- Redux連携やローカルステートでの実装パターンを提供
- カスタマイズ性が高く、色・サイズ・遅延時間を柔軟に調整可能
- API通信、ボタン送信、ページ全体など様々なユースケースに対応
次のステップ:
- より多様なアニメーションが必要な場合は react-spinners を検討
- アクセシビリティ対応を強化する
- パフォーマンス最適化(遅延表示、動的インポート)を実装
ローディング表示は、ユーザーエクスペリエンスを向上させる重要な要素である。本記事の内容を参考に、プロジェクトに最適なローディング実装を実現されたい。