Firebase AuthenticationとVuexの合わせ技バグでハマった
TL;DR
- Vuexのstoreにobjectを渡すときは気を付けよう。
- Vuexのstoreにはできるだけ即値(って呼び方でいいのか?)を入れる。
内容
Firebase authenticationとVuexを使ってこういうのを書いてた。
export default { // ... created() { firebase.auth().onAuthStateChanged((user) => { this.$store.commit('setCurrentUser', { user }); }); }, // ... };
するとChromeのdevtoolが真っ赤になった。
Error in callback for watcher "function () { return this._data.$$state }": "Error: [vuex] Do not mutate vuex store state outside mutation handlers."
しかもこのエラーメッセージ無限に出続ける。
内容はmutationsハンドラ以外からstateをいじるんじゃないとのこと、それはそう。それはそうなんだけど上のコードを見ると分かる通りこれはちゃんとハンドラを通して(commitを使って)stateをいじっている。このcommitの行を消すとエラーも出ないのでこいつが原因らしい。これに結構ハマった。
やったこととか
strictモード外すと消えるけどすげーもにょるのでなんとか考えてた。
似たような問題を調べてて `v-model` だと元のstateが書き換わってそれが問題だとか、取得したstateをそのまま書き換えてるとかはあったけど全然関係ない。
結局エラーログをちゃんと読もうって気持ちになって読むとスタックトレースが `auth.esm.js` とかから始まっている、あれ?
しかもその上の方で `Vue.store._vm.$watch.deep` で死んでるっぽいと言うのが分かる。つまりこれってobjectを渡してるのが原因なのではと気づいてコードを変えてみた。
export default { // ... created() { firebase.auth().onAuthStateChanged((user) => { const { uid, displayName } = user; this.$store.commit('setCurrentUser', { uid, displayName }); }); }, // ... };
要はobjectを直接渡さないようにしてる。これで直った。
つまりはFirebaseのUserオブジェクトってのにいろいろListenerやらObserverとかが生えてて、値が勝手に変わってしまっていたことが原因っぽい、多分。詳しいことはソース読まんと分からん。
これにつきっきりだったわけでもないけど、これのせいで一時間近く進まなかった。
学び
- 認知負荷高めのときは簡単なものにも気づけないので自分を疑う(850394598310239474502938457回目)
- エラーログをちゃんと読もうな。
- とは言いつつエラーメッセージで調べたほうが早い場合もあるので難しい。)
- エラー箇所の特定をちゃんとやる
- エラーログをちゃんと読めば分かったこと
- とはいえ経験の浅いものを組み合わせて使っているので仕方ないかなとも思う。そういうときのエラー対処に時間かかるのはいつものことなのでうまいルーティンがほしいところ。勘に頼ってちゃだめだ。慣れてるならいいけどね。