React

【React】ローディングアニメーション(スピナー)を表示する方法

Reactでローディングアニメーション(スピナー)を表示する方法です。useStateで読み込み中の状態を持ち、trueの間だけCSSの@keyframesで動くスピナーを表示します。fetchと組み合わせてfinallyで消す書き方、React Iconsを使う方法も紹介します。

この記事の目次
  1. Reactでローディング(スピナー)を表示する方法
  2. ファイル構成
  3. index.js
  4. App.css
  5. App.js
  6. 実際にデータを取得するときの書き方
  7. スピナーをアイコンで表示する場合

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を呼ぶ場合は、成功しても失敗してもローディングを消す必要があるため、finallysetLoading(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/aiAiOutlineLoading3Quarters)に、上のspinアニメーションをclassNameで当てるだけでもスピナーになります。

import { AiOutlineLoading3Quarters } from "react-icons/ai";

{loading ? <AiOutlineLoading3Quarters className="spinner-icon" size={32} /> : <Content />}

読み込み中であることを画面読み上げにも伝えたい場合は、スピナー要素にrole="status"aria-label="読み込み中"を付けておくと、見た目だけのアニメーションで終わりません。

スポンサーリンク