Vue.jsでAPIのコールからレスポンスが返ってくるまでぐるぐるマークを出す

# はじめに

久しぶりの投稿すぎて、うまく書けるか分からないですが、
最近触っている Vue.js の投稿をしてみます。

# やってみたこと

現在、Vue CLIでチャットアプリをつくっているのですが、そのアプリでチャンネル変更機能があります。
よくあるチャットアプリと同じイメージで、チャンネルを変更したら変更後のチャンネルのメッセージを取得します。
データ取得・画面表示はできたのですが、どうも操作感が悪いなーと思っていました。

ぐるぐるがなかったためです。

ぐるぐるを表示することで、表示の切り替えのタイミングがユーザーに分かりやすくなり、カクついた操作感は解消されます。また他のボタンを押されることを防ぐこともできます。

# 実際にぐるぐるを表示してみた
![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240563/e7161776-b024-bf66-882b-df5344760f9f.gif)


# ぐるぐるは何を使ったか

ぐるぐる自体は Vuetify が用意してる Progress circular コンポーネントを使用しました。
https://vuetifyjs.com/ja/components/progress-circular/

# どんな処理を入れたか

<!-- 1. Vuetify の Progress circular を表示するだけのコンポーネントを作成。
2. 1 でつくったコンポーネントをコンポーネントの一番親？にあたる App.vue で読み込ませるようにした。
3. ぐるぐるの表示状態を保持するぐるぐる用のストアを作成。
4. axios の interceptors で API コール前後でぐるぐる表示状態を制御した。 -->

1. ぐるぐるコンポーネントを作成
2. ぐるぐるコンポーネントを読み込む
3. ぐるぐる専用のストアを作成
4. axiosのinterceptors内でぐるぐるを制御

## ぐるぐるコンポーネントを作成

```BaseLoading.vue
<template>
  <v-dialog v-model="flag" fullscreen>
    <v-container fluid fill-height class="loading_bg">
      <v-layout justify-center align-center>
        <v-progress-circular :indeterminate="true" size="64" color="accent" />
      </v-layout>
    </v-container>
  </v-dialog>
</template>
<script lang="ts">
import Vue from "vue";
import { Component, Prop } from "vue-property-decorator";

@Component({
  components: {},
})
export default class BaseLoading extends Vue {
  @Prop({}) public flag: boolean;

  constructor() {
    super();
    this.flag = false;
  }
}
</script>
```
1つbooleanの値をもち、その値で表示/非表示を切り替えるだけのコンポーネントです。
その値がどこからどのタイミングでpropされるかは後述します。

## ぐるぐるコンポーネントを読み込む
全画面共通でサーバとの通信時はぐるぐるを表示したいため、コンポーネントの一番親？にあたるApp.vueで読み込みます。
ぐるぐるコンポーネントに渡すbooleanは、次に説明する、ぐるぐる専用のストアのstateを見ています。
つまり、このApp.vueではストアのstateが更新されたタイミングでぐるぐるコンポーネントにbooleanをpropすることができます。

```App.vue
<template>
  <v-app id="inspire">
    <v-content>
      <router-view />
      <BaseLoading :flag="IsShow" />
    </v-content>
  </v-app>
</template>

<script lang="ts">
import Vue from "vue";
import Component from "vue-class-component";
import BaseLoading from "./components/BaseLoading.vue";
import LoadingStore from "./store/loading/index";

@Component({
  components: { BaseLoading },
})
export default class App extends Vue {
  constructor() {
    super();
  }

  get IsShow() {
    return LoadingStore.IsShow;
  }
}
</script>
```

## ぐるぐる専用のストアを作成
中身はシンプルで、booleanのstateが1つと、そのstateに対するgetter/setter(mutation)のみ用意します。

```loading/index.ts
import {
  Module,
  VuexModule,
  Mutation,
  Action,
  getModule,
} from "vuex-module-decorators";

import store from "..";

@Module({ dynamic: true, store, name: "loadingStore", namespaced: true })
class LoadingStore extends VuexModule {
  private isShow: boolean = false;

  @Mutation
  public setIsShow(flag: boolean) {
    this.isShow = flag
  }

  get IsShow(): boolean {
    return this.isShow;
  }
}
exort default getModule(LoadingStore);
```

## axiosのinterceptors内でぐるぐるを制御

このアプリでは、Vueで非同期処理をするときの定番であるaxiosを使っています。
[axios を利用した API の使用](https://jp.vuejs.org/v2/cookbook/using-axios-to-consume-apis.html)

axiosでは、interceptorsなるものが用意されており、APIコールのリクエスト直前とレスポンス直後の処理を入れることができます。

```interceptors.ts
export default function setup() {

  axios.interceptors.request.use(async (config) => {
    LoadingStore.setIsShow(true)
    return config;
  });

  axios.interceptors.response.use(
    (response) => {
      LoadingStore.setIsShow(false)
      return response;
    },
    async (error: AxiosError) => {
      LoadingStore.setIsShow(false)
      }
  );
}
```
リクエスト直前

```
LoadingStore.setIsShow(true)
```

レスポンス直後

```
LoadingStore.setIsShow(false)
```

これで、APIの通信時のみぐるぐるを表示することができました。


## 今後の課題
1操作で1APIコールならこれで問題ないのですが、1操作で複数APIコールする箇所があり、この処理のままだと、ぐるぐるがチカチカする感じで少し違和感があります。
それはまた追々。。