Redux ToolkitでLoadingの状態を管理する 

# 何をやるか
SPAを扱う上で、どのタイミングでローディングの表示をするのか、どのように管理するのか。
結構考えるのめんどくさいと思います。
↓こんなやつ
![b39f6ff59f51a613294a5f21ef9382de.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325594/9821506e-e56d-df4e-d5ba-b9119d8fc787.gif)


Redux Toolkitを使えばシンプルにかけたので備忘録として残しておきます:writing_hand:
どのようにローディング状態管理しよう…とお考えの方がいれば、その解決の一つになればと思います:eyes:


# 使用している技術

- React (reactHooks)
- Redux (Redux-toolkit)
- TypeScript


# 機能
今回はAPIを叩いてpending中にloadingを表示、
処理が終了したらloadingを非表示という機能を想定して書いていきます。

redux toolkitが用意してくれている`extraReducers`という機能を使って実装します！

pending    → 実行開始〜結果が帰ってくるまでの間
fulfilled  → 正常終了
rejected   → エラー

となっているので、以下のように、
pendingの時のみ、isLoadingという項目をtrueにすることで、
APIが実行開始〜結果が帰ってくるまでの間の状態を管理します。

```sampleSlice.tsx

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

```

```fetch.ts

// 別に切り分けた関数
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を表示させるコンポーネントです。
（待っている間はユーザー側の入力ができないので、ユーザー操作できるようにするには調整必要です）

```fixedSpinner.tsx
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%);
`
```

```Spinner.tsx
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の場合のみ、先程作成したスピナーを表示します。

```sample.tsx

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の管理ができてハッピーでした:clap:
コンポーネントも使いまわせるので、使える部分はこれ使っていこうかな〜と考えております。

もっといい方法あればご教示ください:pray: