【React】Material UIを用いてローディング画面を実装する

# 背景
重いサイトを開く際には時間がかかること事があり、
ローディング画面をいったん挟むことがあります。
本記事ではWebサイトのローディング時に疑似的なローディング画面を作成する内容を紹介します。

# 作成するもの
![animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/d9edca13-b3dc-253b-955a-aefbf967dfd9.gif)


# 環境
- Vite：5.0.8
- Typescript：5.2.2
- @emotion/react：11.11.3
- @emotion/styled：11.11.0
- @mui/joy：5.0.0-beta.21


# プロジェクトの作成
以下のコマンドでReact＋TypeScriptのプロジェクトを作成します。
```
npm create vite@latest
```

https://ja.vitejs.dev/guide/


# MUIのインストール
Material UIのインストールします。
```
npm install @mui/joy @emotion/react @emotion/styled
```

https://mui.com/joy-ui/getting-started/installation/


# ローディング中の画面
ローディング中のコンポーネントを作成しています。

```react:Loading.tsx
import LinearProgress from '@mui/joy/LinearProgress';

function LoadingScreen() {
    return (
      <div className='LoadMain'>
        <div className='progressBarContainer'>
          <h2>Now Loading・・・</h2>
          <LinearProgress size="lg"/>
        </div>
      </div>
    )
}

export default LoadingScreen;
```

以下の部分でプログレスバーを作成しています。
`size`でバーの太さを調整（lg：太）しています。
```react
<LinearProgress size="lg"/>
```
プログレスバーの詳細はドキュメントに記載されています。

https://mui.com/joy-ui/react-linear-progress/

以下のCSSでアニメーションを割り当てています。
`animation`プロパティに透明度が1から0に変化する`fadeout`のキーフレームを当てています。

```css:App.css
.LoadMain {
  height: 100vh;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: pink;
  animation: fadeout 6s;
}

@keyframes fadeout {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

.progressBarContainer {
  text-align: center;
  width: 50%;
}
```


# ローディング後の画面
ローディング後のコンポーネントを作成しています。
```react:Loaded.tsx
function LoadedScreen() {
  return (
    <div className='LoadedMain'>
      <h1>ロード後の画面</h1>
    </div>
  )
}

export default LoadedScreen;
```

以下のCSSの`animation`プロパティに、
透明度が0から1に変化するキーフレームのアニメーションを当てています。
これで徐々にローディング後の画面が表示されます。

```css:App.css
.LoadedMain {
  height: 100vh;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: lightblue;
  animation: fadein 4s;
}

@keyframes fadein {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
```


# Appコンポーネント
Appコンポーネントの全体は以下の通りです。
ローディングから5秒後に`setLoading(false)`が呼び出され、
状態変数`loading`が`true`から`false`に更新されます。
三項演算子で`loading`が`true`の場合はLoadingScreen（ローディング中）を、
`false`の場合はLoadedScreen（ローディング後）を返すようにします。

```react:App.tsx
import { useEffect, useState } from 'react';

// 外部関数のインポート
import LoadingScreen from './Loading';
import LoadedScreen from './Loaded';

// 外部ファイルのインポート
import './App.css';

export default function App() {
  const [loading, setLoading] =useState(true);

  useEffect(() => {
      const timeoutId = setTimeout(() => {
        setLoading(false)
      }, 5000);

      return () => clearTimeout(timeoutId);
  }, []);

  return (
    <div >
      {loading ? (<LoadingScreen/>):(<LoadedScreen/>)}
    </div>
  );
}
```

<details><summary>main.tsx</summary>

main.tsxのコードは以下の通りです。

```react:main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)
```
</details>

# 実行結果
次のコマンドでプロジェクトを起動し、挙動を確認します。
```
npm run dev
```
画像のように疑似的なローディング画面を作成できました。

![animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/a779617e-54e8-ed89-b3fd-c8dc27f4f752.gif)