Nuxt.jsでMaximum call stack size exceededというエラーに悩まされた
最近、nuxt.jsを使って個人サービス開発に励んでおります。
がしかし、
Javascriptをほとんど触ってこなかった人なのでこのエラーには大変苦戦しました。
その解決に至った経路だとか方法を備忘録として書き留めます。
どんな風にエラーが起きたか
エラーが生じたときの状態は
ヘッダーにあるv-list-item
から@click
でアクションを実行しログアウト。
トップページ的なところに遷移するはずがエラー画面に。
developer toolをみると、こんなエラーが
RangeError: Maximum call stack size exceeded at VueComponent.logout (header.vue?24d4:71) at VueComponent.logout (header.vue?24d4:72) at VueComponent.logout (header.vue?24d4:72) at VueComponent.logout (header.vue?24d4:72) at VueComponent.logout (header.vue?24d4:72) at VueComponent.logout (header.vue?24d4:72) at VueComponent.logout (header.vue?24d4:72) at VueComponent.logout (header.vue?24d4:72) at VueComponent.logout (header.vue?24d4:72) at VueComponent.logout (header.vue?24d4:72)
エラーからして、logoutが何回も実行されてるようにみえる。。
つまり何らかの理由で
無限ループしていてスタックサイズ超えちゃったよ、あーあ、
ってわけですね。
エラーが起きたソースコード
ソースコードの説明は省略してます。
index.js
// storeを定義してるモジュールを読み込むファイル import Vuex from 'vuex' import user from './modules/user' import posts from './modules/posts' export default () => new Vuex.Store({ modules: { user, posts } })
user.js
// userに関するstoreを定義してるモジュール const state = { user: null, loginStatus: true // ログイン状態を擬似的に表現してたので最初からtrue } const getters = { user: (state) => state.user, isLogin: (state) => state.loginStatus } const mutations = { setUser(state, { user }) { state.user = user }, login(state) { state.loginStatus = true }, logout(state) { state.loginStatus = false state.user = null } } const actions = { fetchUser({ commit }, user) { commit('setUser', {user}) }, login({ commit }) { commit('login') }, logout({ commit }) { commit('logout') } } export default { namespaced: true, state, getters, actions, mutations }
Header.vue
<!-- Header component 全くAtomic Design意識してないから参考にしないでください。 --> <template> <div> <v-app-bar color="primary" > <v-toolbar-title> <nuxt-link to='/'> <img src="~/static/weblogo.png"> </nuxt-link> </v-toolbar-title> <v-spacer></v-spacer> <v-menu offset-y> <template v-slot:activator="{ on }"> <v-btn icon color="transparent" v-on="on" > <v-app-bar-nav-icon></v-app-bar-nav-icon> </v-btn> </template> <v-list v-if="loginStatus"> <v-list-item nuxt to='#' > <v-list-item-title>マイページ</v-list-item-title> </v-list-item> <v-list-item nuxt to='/' @click="logout" inactive > <v-list-item-title>ログアウト</v-list-item-title> </v-list-item> </v-list> <v-list v-else> <v-list-item nuxt to='/login' > <v-list-item-title>ログイン</v-list-item-title> </v-list-item> <v-list-item nuxt to='/signup' > <v-list-item-title>会員登録</v-list-item-title> </v-list-item> </v-list> </v-menu> </v-app-bar> </div> </template> <script> import { mapState, mapGetters, mapActions } from "vuex"; export default { name: "Header", computed: { ...mapState({ user: state => state.user.user, loginStatus: state => state.user.loginStatus }) }, methods: { ...mapActions('user',[ "logout" ]), logout: function() { this.logout() } } } </script>
バックエンドばかり普段触ってる身からすると、
無限ループになりそうなところはないので非常に原因が何か分かりづらかったです。
(解決後には「確かに無限ループするじゃん!」ってなりましたw)
解決策
で、色々ググってたらこのサイトのおかげで解決ができました。
あ、確かに〜、と腑に落ちた原因でした。
何がいけなかったのかというと、
methods: { ...mapActions('user',[ "logout" // user storeで定義されてるmutation実行のためのアクション ]), logout: function() { //コンポーネントからログアウトを発火させるためのメソッド this.logout() // user storeで定義されてるmutation実行のためのアクション }
このような意図でコンポーネント内でStoreのアクションを実行するメソッドを定義していましたが、
実際this.logout()
はコンポーネント内のlogoutメソッドと解釈されてしまうようでした。
要するにlogout()が自分自身を呼び続けていたために起きてしまったエラーでした。
そりゃ無限ループになりますよね。
実際のところ、
...mapActions('user',[ "logout" ])
この宣言だけで、
コンポーネント内でStoreで定義してるアクションは使用できるようになってるみたいなので、
Header.vueのJavascriptの部分を下記のように直すことで解決でした。
<script> import { mapState, mapGetters, mapActions } from "vuex"; export default { name: "Header", computed: { ...mapState({ user: state => state.user.user, loginStatus: state => state.user.loginStatus }) }, methods: { ...mapActions('user',[ "logout" ]) } } </script>