Vuexでasync/awaitを用いて値を返すアクションを記述する

何を今更ということかもしれないけれど、自分にとってかなりの:sunrise:新しい地平が開けた感:sunrise:があったので一応記事として残しておくことにしました。

# そもそもVuexのアクションとは？

https://vuex.vuejs.org/ja/guide/actions.html

> アクションは、状態を変更するのではなく、ミューテーションをコミットします。
> アクションは任意の非同期処理を含むことができます。

自分もアクションはミューテーションをコミットしたりするものだと思ってました(その通りです)。
ユーザが何か操作すると、それをトリガーにアクション、ミューテーション、ステートと処理が伝播していき、ステートの変化がUIに反映されたりして、ユーザに伝わるというのが通常のVuexの流れだと思います。
そのようにVuexを書いていたあるとき、Vuexの複数のアクションの処理が抽象化出来そうな状況が生まれました:eyes:
そこでふと立ち止まってしまいました。アクションの返り値は何なのだと:thinking:

# 抽象化出来そうなアクション

そこで、CodeSandboxで以下のようなVuexコードを試してみました:pencil:

https://codesandbox.io/s/vue

```js
import Vue from "vue";
import App from "./App.vue";
import Vuex from "vuex";

Vue.config.productionTip = false;

Vue.use(Vuex);

const store = {
  state: {
    text: "hello"
  },
  actions: {
    doubleText({ state }) {
      let newText = [];
      for (let i = 0; i < 2; i++) {
        newText.push(state.text);
      }
      newText = newText.join("!! ") + "!!";
      console.log(newText);
    },
    tripleText({ state }) {
      let newText = [];
      for (let i = 0; i < 3; i++) {
        newText.push(state.text);
      }
      newText = newText.join("!! ") + "!!";
      console.log(newText);
    }
  }
};

new Vue({
  render: h => h(App),
  store: new Vuex.Store(store),
  mounted() {
    this.$store.dispatch("doubleText");
    this.$store.dispatch("tripleText");
  }
}).$mount("#app");
```

2つのアクション内で、文字列をループで回して増やしている部分を共通化出来そうです。

```js
import Vue from "vue";
import App from "./App.vue";
import Vuex from "vuex";

Vue.config.productionTip = false;

Vue.use(Vuex);

const store = {
  state: {
    text: "hello"
  },
  actions: {
    multiText({ state }, num) {
      let newText = [];
      for (let i = 0; i < num; i++) {
        newText.push(state.text);
      }
      return newText.join("!! ") + "!!";
    },
    doubleText({ dispatch }) {
      const text = dispatch('multiText', 2);
      console.log(text);
    },
    tripleText({ dispatch }) {
      const text = dispatch('multiText', 3);
      console.log(text);
    }
  }
};

new Vue({
  render: h => h(App),
  store: new Vuex.Store(store),
  mounted() {
    this.$store.dispatch("doubleText");
    this.$store.dispatch("tripleText");
  }
}).$mount("#app");
```

単純にまとめてコンソールを確認すると、Promiseが返ってきました。
な、なるほどーー :flushed:

# async/awaitを用いたアクション

ということは、async/awaitが使えるということで、
実はこれは公式ドキュメントにも書かれています:thumbsup:

https://vuex.vuejs.org/ja/guide/actions.html#%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E6%A7%8B%E6%88%90%E3%81%99%E3%82%8B

```js
import Vue from "vue";
import App from "./App.vue";
import Vuex from "vuex";

Vue.config.productionTip = false;

Vue.use(Vuex);

const store = {
  state: {
    text: "hello"
  },
  actions: {
    multiText({ state }, num) {
      let newText = [];
      for (let i = 0; i < num; i++) {
        newText.push(state.text);
      }
      return newText.join("!! ") + "!!";
    },
    async doubleText({ dispatch }) {
      const text = await dispatch('multiText', 2);
      console.log(text);
    },
    async tripleText({ dispatch }) {
      const text = await dispatch('multiText', 3);
      console.log(text);
    }
  }
};

new Vue({
  render: h => h(App),
  store: new Vuex.Store(store),
  mounted() {
    this.$store.dispatch("doubleText");
    this.$store.dispatch("tripleText");
  }
}).$mount("#app");
```

そこに書いていいのかという疑問はありつつも、これでアクションに普通の関数(のようなもの)が書けました:thumbsup:
ひとつ賢くなりました:raised_hands::two_hearts::beers::sushi::kissing_heart: