Reactでローディング(スピナー)を表示する方法を紹介します。
結論から書くと、Reactのローディングアニメーションは「読み込み中かどうか」をuseStateで持ち、trueの間だけスピナー用の要素を表示する、という2つの組み合わせで作れます。
アニメーションそのものはCSSの@keyframesで回す(または上下させる)だけなので、ライブラリは不要です。
データ取得が終わったらsetLoading(false)にして、本来の内容に切り替えます。
Reactでローディング(スピナー)を表示する方法
ファイル構成
src
∟App.css
∟App.js
∟index.js
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
上でAppを読み込みます。
index.jsは特にいじるところはありません。
App.css
/* ローディング */
.loader,
.loader:before,
.loader:after {
background: #ffffff;
-webkit-animation: load1 1s infinite ease-in-out;
animation: load1 1s infinite ease-in-out;
width: 1em;
height: 4em;
}
.loader {
color: #ffffff;
text-indent: -9999em;
margin: 88px auto;
position: relative;
font-size: 11px;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
}
.loader:before,
.loader:after {
position: absolute;
top: 0;
content: '';
}
.loader:before {
left: -1.5em;
-webkit-animation-delay: -0.32s;
animation-delay: -0.32s;
}
.loader:after {
left: 1.5em;
}
@-webkit-keyframes load1 {
0%,
80%,
100% {
box-shadow: 0 0;
height: 4em;
}
40% {
box-shadow: 0 -2em;
height: 5em;
}
}
@keyframes load1 {
0%,
80%,
100% {
box-shadow: 0 0;
height: 4em;
}
40% {
box-shadow: 0 -2em;
height: 5em;
}
}
上のCSSは3本のバーが上下するタイプです。もっと単純な「円がくるくる回る」スピナーなら、次の数行で足ります。
.spinner {
width: 40px;
height: 40px;
margin: 88px auto;
border: 4px solid #e5e7eb;
border-top-color: #2563eb;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
ローディングのcssは自分で実装してもいいですし、
下のようなまとめサイトもあるので、そこから借りてくるのもよいと思います。
参考サイト: CSSで実装されたローディング・スピナーのアニメーションのまとめ
App.js
import { useEffect, useState } from "react";
import "./App.css";
function App() {
const [loading, setLoading] = useState(true); //最初にloading出したいのでtrue
useEffect(() => {
const fetchData = async () => {
// この辺りに非同期の処理(fetchなど)
setLoading(false);
};
fetchData();
}, []);
return (
<>
<div className="App">
{loading ? (
<p className="loader">ロード中。。。</p>
) : (
<>
<div className="">
ここに表示したい内容
</div>
</>
)}
</div>
</>
);
}
export default App;
ローディングの状態を保持したいので、useStateを使います。
初期値は、最初にローディングを出したいのでtrueにします。
そして、JSX部分で、三項演算子を使って、loadingがtrueのときとfalseのときで、表示を切り替えます。
三項演算子や&&での出し分けの書き方は、Reactでよく使う条件分岐の記述方法にまとめています。
あとは、関数内で、setLoadingにtrueまたはfalseをセットすれば、ローディングの状態を切り替えられます。
実際にデータを取得するときの書き方
fetchでAPIを呼ぶ場合は、成功しても失敗してもローディングを消す必要があるため、finallyでsetLoading(false)にします。
useEffect(() => {
const fetchData = async () => {
try {
const res = await fetch("/api/items");
const data = await res.json();
setItems(data);
} catch (error) {
console.error(error);
} finally {
setLoading(false); // 成功・失敗どちらでも消す
}
};
fetchData();
}, []);
finallyを使わずtryの中だけでsetLoading(false)にすると、通信に失敗したときスピナーが回り続けます。
取得したデータが配列なら、表示側ではmapで並べます。書き方はReactで配列を表示する方法を参考にしてください。
スピナーをアイコンで表示する場合
CSSを自分で書かずに済ませたいなら、React Iconsの回転系アイコン(たとえばreact-icons/aiのAiOutlineLoading3Quarters)に、上のspinアニメーションをclassNameで当てるだけでもスピナーになります。
import { AiOutlineLoading3Quarters } from "react-icons/ai";
{loading ? <AiOutlineLoading3Quarters className="spinner-icon" size={32} /> : <Content />}
読み込み中であることを画面読み上げにも伝えたい場合は、スピナー要素にrole="status"とaria-label="読み込み中"を付けておくと、見た目だけのアニメーションで終わりません。