きっかけ
今までクラシックモードで書いていたstoreをモジュールモードで書くことにしました。
index.jsの中でarticle.jsというファイルをmoduleとしてimportしたあと、
componentでactionを実行しようとしたら[vuex] unknown action type
というエラーが出たので、その対処法およびデバッグ法をご紹介します。
ファイル
import Vuex from "vuex";
import articles from "./modules/articles"
const store = () => new Vuex.Store({
modules: {
articles
}
})
export default store
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
}
<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です
<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の各定数(変数)をObjectではなくfunctionとして定義していたから
です。
functionとして定義(動かなかった)
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として定義(動いた)
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もあるはず。
と考え、確認してみました。以下が確認結果となります(長いので大部分省略しています)
<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表記に戻して確認してみると、
<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]
}
となり、やはり_actions, _mutations, _wrappedGettersがactions, mutations, gettersのstoreである
ことが実際にわかると思います。
まあstoreの中に入っているので当然といえば当然ですが、これを知っているだけでもデバッグは捗るような気がしています。
まとめ
1. unknown action typeが出る場合は、state, actions, mutations, gettersの定義を確認してみよう
→ functionではなくobjectとして登録するのが正解
2. actions, mutations, gettersが読み込まれているかを確認するときは、nuxtContextのstore内部の
_actions, _mutations, _wrappedGetters`の中身を確認してみよう