何をやるか
SPAを扱う上で、どのタイミングでローディングの表示をするのか、どのように管理するのか。
結構考えるのめんどくさいと思います。
↓こんなやつ

Redux Toolkitを使えばシンプルにかけたので備忘録として残しておきます
どのようにローディング状態管理しよう…とお考えの方がいれば、その解決の一つになればと思います
使用している技術
- React (reactHooks)
- Redux (Redux-toolkit)
- TypeScript
機能
今回はAPIを叩いてpending中にloadingを表示、
処理が終了したらloadingを非表示という機能を想定して書いていきます。
redux toolkitが用意してくれているextraReducersという機能を使って実装します!
pending    → 実行開始〜結果が帰ってくるまでの間
fulfilled  → 正常終了
rejected   → エラー
となっているので、以下のように、
pendingの時のみ、isLoadingという項目をtrueにすることで、
APIが実行開始〜結果が帰ってくるまでの間の状態を管理します。
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"
import { fetchGet } from "./common/fetch"
interface selectUserProps {
  isLogin: boolean
}
// ↓このAPIを叩く想定で進める
export const getInfo = createAsyncThunk("admin/post",async () => {
  const url = "叩くAPIのURL"
  const result = await fetchGet(url)
  return result
  }
)
const sampleSlice = createSlice({
  name: "sample",
  // ↓初期値
  initialState: {
    isLoading:false
  },
  
  // extraReducersで状態をLoading管理する
  extraReducers: (builder) => {
    builder.addCase(getInfo.pending, (state, action) => {
      // APIの処理が始まったら isLoadingをtrueに
      state.isLoading = true
    })
    builder.addCase(getInfo.rejected, (state, action) => {
      // 失敗したら isLoadingをfalseに 
      state.isLoading = false
    })
    builder.addCase(getInfo.fulfilled, (state, action) => {
      // 成功しても isLoadingをfalseに 
      state.isLoading = false
    })
  },
})
// ローディング状態をexport
export const selectLoading = (state: any): selectUserProps => state.sample.isLoading
export default sampleSlice.reducer
// 別に切り分けた関数
import axios from "axios"
axios.defaults.withCredentials = true
export const getInfo = async(url:string) =>{
  const result = await axios(url, {
    method: 'GET',
    )
  return result.data
}
UI
UI上では、スピナーを作成して、
先程のloading状態をインポートし、trueの場合だけ作成したスピナーを表示させます。
スピナー
画面全体にLoadingを表示させるコンポーネントです。
(待っている間はユーザー側の入力ができないので、ユーザー操作できるようにするには調整必要です)
import React, { useEffect, useState } from 'react'
import { css } from '@emotion/core'
import { Spinner } from '../../components/atoms/Spinner'
export const FixedSpinner = () => {
  return (
    <div css={spinnerOverRay}>
      <div css={spinnerWrap}>
        <Spinner />
      </div>
    </div>
  )
}
const spinnerOverRay = css`
  position: fixed;
  -ms-transform: translate(-50%, -50%);
  height: 100vh;
  width: 100vw;
  z-index: 999;
  background-color: rgba(245, 245, 245, 0.5);
`
const spinnerWrap = css`
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
`
import React from 'react'
import { css } from '@emotion/core'
export const Spinner = () => {
  return <div css={spinner} />
}
export const spinner = css`
  width: 50px;
  height: 50px;
  border: 10px solid #dddddd;
  border-top-color: #aaaaaa;
  border-radius: 50%;
  animation: 1s spin infinite linear;
  @keyframes spin {
    from {
      transform: rotateZ(0deg);
    }
    to {
      transform: rotateZ(360deg);
    }
  }
`
Loading表示
useSelectorを使って、loading状態を取得し、
trueの場合のみ、先程作成したスピナーを表示します。
import React from 'react'
// components
import { FixedSpinner } from '../../components/block/FixedSpinner'
// store
import { getInfo, selectLoading } from '../../../stores/slices/sampleSlice'
export const Sample = ({ setPath }: any) => {
  const dispatch = useDispatch()
  const loading = useSelector(selectLoading)
  const handleSubmit = () => {
    dispatch(getInfo())
  }
  return (
    <div>
      // loading が true の時、FixedSpinnerを表示
      {loading && <FixedSpinner />}
       <button css={SubmitBtn} onClick={handleSubmit}>
         API叩く
       </button>
    </div>
  )
}
感想
Redux Toolkitを使えば簡単にloadingの管理ができてハッピーでした
コンポーネントも使いまわせるので、使える部分はこれ使っていこうかな〜と考えております。
もっといい方法あればご教示ください

