[Vuex/Nuxt] storeをmodule化したときに[vuex] unknown action typeが出たときの対処法およびデバッグ法

## きっかけ
今までクラシックモードで書いていたstoreをモジュールモードで書くことにしました。
index.jsの中でarticle.jsというファイルをmoduleとしてimportしたあと、
componentでactionを実行しようとしたら`[vuex] unknown action type`
というエラーが出たので、その対処法およびデバッグ法をご紹介します。


## ファイル

```js:store/index.js
import Vuex from "vuex";
import articles from "./modules/articles"

const store = () => new Vuex.Store({
  modules: {
    articles
  }
})

export default store
```

```js:store/modules/article.js
const namespaced = true

const state = () => ({
  articles: []
})

const mutations = () => ({
  SET_ARTICLES(state, articles) {
    state.articles = articles
  }
})

const actions = () => ({
  setArticles(vuexContext, articles) {
    vuexContext.commit("SET_ARTICLES", articles)
  },
})

const getters = () => ({
  getArticles(state) {
    return state.articles
  }
})

export default {
  namespaced,
  state,
  mutations,
  actions,
  getters
}
```

```vue:pages/index.vue
<template>
  <div>
    <Header />
    <ArticleList :articles="articles" />
  </div>
</template>

<script>
import Header from '@/components/molecules/Header.vue'
import ArticleList from '@/components/organisms/ArticleList.vue'
import { mapGetters } from 'vuex'

export default {
  name: 'TopPage',
  components: {
    Header,
    ArticleList
  },
  async fetch({store, $axios}) {
    await $axios.$get("http://localhost:3000/api/v1/articles")
      .then((data) => {
        store.dispatch("articles/setArticles", data)
      })
      .catch(e => {
        console.log(e)
      })
  },
  computed: {
    ...mapGetters({
      articles: "articles/getArticles"
    }
  }
}
</script>

```

この状態でサーバにアクセスしたところ、
`[vuex] unknown action type 'setArticles' undefined`
が出てしまいました。
`pages/index.vue`のfetchのthen内部のdispatchが呼び出せていないようです。
しかし、moduleの設定などは確認したところすべて合っていました。

## 自分がしたこと
そもそも、setArticlesが読み込めていないということは、
`1. article.jsでのsetArticlesメソッドの設定がおかしく読み込めていない`
`2. そもそもarticle.jsをmoduleとして読み込めていない`
可能性があるので、まずは2番から確認していくことにしました。

## 「2. そもそもarticle.jsをmoduleとして読み込めていない」について
これは、axiosでdataを取得したあとのthenで一度 contextを確認してみればいいと感じました。

pages/index.vueのthenにconsole.logをはさみます。
※このcontextはnuxtContextです


```vue:pages/index.vue
<script>
...
  async fetch(context) {
    await $axios.$get("http://localhost:3000/api/v1/articles")
      .then((data) => {
        console.log(context.store.state) // ここです
        store.dispatch("articles/setArticles", data)
      })
      .catch(e => {
        console.log(e)
      })
...
</script>
```

結果としては、以下のオブジェクトが返ってきました。

```
{ article: { articles: [] } }
```

なので、`article.js自体はmoduleとして正しく読み込まれている` ことがわかりました。

## 「`1. article.jsでのsetArticlesメソッドの設定がおかしく読み込めていない`」について
となれば、あとはsetArticlesメソッドの部分を確認します。

これ、もう結論を言ってしまうのですがなぜ動かなかったのかというと、
article.jsの各定数（変数）を<b>`Objectではなくfunctionとして定義していたから`</b>です。


### functionとして定義（動かなかった）
```js:store/modules/article.js
const namespaced = true

const state = () => ({
  articles: []
})

const mutations = () => ({
  SET_ARTICLES(state, articles) {
    state.articles = articles
  }
})

const actions = () => ({
  setArticles(vuexContext, articles) {
    vuexContext.commit("SET_ARTICLES", articles)
  },
})

const getters = () => ({
  getArticles(state) {
    return state.articles
  }
})

export default {
  namespaced,
  state,
  mutations,
  actions,
  getters
}
```

### Objectとして定義（動いた）

```js:store/modules/article.js
const namespaced = true

const state = {
  articles: []
}

const mutations = {
  SET_ARTICLES(state, articles) {
    state.articles = articles
  }
}

const actions = {
  setArticles(vuexContext, articles) {
    vuexContext.commit("SET_ARTICLES", articles)
  },
}

const getters = {
  getArticles(state) {
    return state.articles
  }
}

export default {
  namespaced,
  state,
  mutations,
  actions,
  getters
}
```

## actions, mutationsのデバッグについて
一番最初に確認した2.のケースではstateが正常に読み込まれていたので、
stateに関してはアロー関数式で表現しても問題ないように思いました。

が、actionsとmutations、gettersに関してはアロー関数式で定義するとunknown action typeが出てしまいます。

その際に、`「どのようにしてactionsとmutations, gettersが正しく読み込まれているのかを知るのか」`がわかりませんでしたが、
contextを確認した際にその確認方法がわかった気がしましたので、ここに載せておきます。


先程2.を確認した際、`context.store`を確認しましたが、
このstoreの中にstateがあるならactionsやmutations, gettersもあるはず。
と考え、確認してみました。以下が確認結果となります（長いので大部分省略しています）

```js:console.log(context.store)
<ref *5> Store {
  _committing: false,
  // 3行目------
  _actions: [Object: null prototype] {},
  _actionSubscribers: [],
  _mutations: [Object: null prototype] {},
  _wrappedGetters: [Object: null prototype] {},
  // ----------
  _modules: ModuleCollection {
    root: Module {
      runtime: false,
      _children: [Object: null prototype],
      _rawModule: [Object],
      state: [Object],
      context: [Object]
    }
  },
  // 下から9行目
  _modulesNamespaceMap: [Object: null prototype] {
    'articles/': Module {
      runtime: false,
      _children: [Object: null prototype] {},
      _rawModule: [Object],`
      state: [Object],
      context: [Object]
    }
  },
```

下から9行目、` _modulesNamespaceMap`のところに`'articles/': Module`とあるので、articles.jsが正しくmoduleとして読み込まれている、ということでしょう。

そして、3行目から下4行を見ると、`_actions`, `_mutations`, `_wrappedGetters`があり、いずれも` [Object: null prototype] {},`となっています。
これは2.で確認した失敗のケースなので、actions, mutations, gettersともに登録されておらず`{}`となっているのでしょう。
おそらくここがactions, mutations, wrappedGettersのstoreだろうという推測のもと、それらをfunctionからobject表記に戻して確認してみると、

```js:console.log(context.store)
<ref *5> Store {
  _committing: false,
  _actions: [Object: null prototype] {
    'articles/setArticles': [ [Function: wrappedActionHandler] ]
  },
  _actionSubscribers: [],
  _mutations: [Object: null prototype] {
    'articles/SET_ARTICLES': [ [Function: wrappedMutationHandler] ]
  },
  _wrappedGetters: [Object: null prototype] {
    'articles/getArticles': [Function: wrappedGetter]
  }
```
となり、やはり<b>`_actions, _mutations, _wrappedGettersがactions, mutations, gettersのstoreである`</b>ことが実際にわかると思います。

まあstoreの中に入っているので当然といえば当然ですが、これを知っているだけでもデバッグは捗るような気がしています。


## まとめ
<b>1. unknown action typeが出る場合は、state, actions, mutations, gettersの定義を確認してみよう</b>
→ functionではなくobjectとして登録するのが正解
<b>2. actions, mutations, gettersが読み込まれているかを確認するときは、nuxtContextのstore内部の
_actions, _mutations, _wrappedGetters`の中身を確認してみよう</b>