createdでstoreを呼び出してもundefinedになるんだが?
はい、タイトル通りです。
グローバルで使いたいオブジェクトをVuex
のstore
に保存して、
任意のタイミングで呼び出したかったのですが、undefined
とうまく取り出せませんでした。
その原因と解決策について、備忘録として残したいと思います。
やりたいこと
Vuex
のstore
にオブジェクトを保管して、他のコンポーネントでも使用したい。
今回はログインしたユーザーオブジェクトをstore
に保存して、
ユーザー情報を他のコンポーネントでも自由に取り出したい。
名前とか、プロフィール画像とか。
前提条件
storeの設定
store
は以下のように、ユーザーオブジェクトをstate
にセットしている。
コンポーネント側ではmapGetters
を使って、state
の値を参照する。
import { createStore } from 'vuex'
const store = createStore({
state () {
authUser: null
},
getters: {
authUser: state => state.authUser;
},
mutations: {
// 認証済みユーザー情報をセット
setAuthUser (state, AuthUser) {
state.authUser = AuthUser;
}
},
actions: {},
}
})
export default store
保存するオブジェクト
ユーザーオブジェクトは以下のようなデータ
Object: {
'id': 1,
'first_name': "山田",
'last_name': "太郎",
'email': "test@example.jp",
'hashed_password': "$2a$12$omQoqe9J/ex5KrK6LAHwPeLLONg2Nha5xmTRBtn4pVs.gL9nWyECC",
'created_at': "2023-01-22T06:51:05.189Z",
'updated_at': "2023-01-22T06:51:05.189Z",
}
うまくいかなかったパターン
色々試してみた結果、以下のことがわかった。
・created
とmounted
の両方でstate
を参照するとundefine
・テンプレート内で{{}}
による呼びだしが可能だが、console
にundefine
のエラーが表示
・クリックイベントではstore
のデータを参照できる。
よって、Vuex
内のstate
がセットされるのは
created()
, mounted()
のライフサイクルメソッドの後っぽい。
import { mapGetters } from "vuex";
const headerComp = {
template:
`
<div class="comp-header">
<div class="lay-header">
<div class="lay-header-right">
<ul>
<!-- オブジェクトが表示されている -->
<li>{{this.user}}</li>
<!-- クリックイベントでオブジェクト情報をconsoleに表示する -->
<li @click="getUser">{{this.user}}</li>
</ul>
</div>
</div>
</div>
`,
created() {
// undefinedになる
console.log(this.authUser);
},
mounted() {
// undefinedになる
console.log(this.authUser);
},
computed: {
...mapGetters(["authUser"]),
},
methods: {
// オブジェクトを取り出せる
getUser(){
console.log(this.authUser);
},
}
}
export default headerComp
解決策
Vuex
のインスタンスメソッドである watch
メソッドを使用して、
store
のstateデータ
が変更された瞬間を検知して、stateデータ
を取得する。
watch とは
store
データが更新されたときにコールバック関数を呼び出すことができます。
watch(fn: Function, callback: Function, options?: Object): Function
- 第一引数には、
store
のstateデータ
を - 第二引数には、第一引数の返り値を受け取り、適当な処理を
- 第三引数には、オプションを追加することができます。
詳しい説明は公式ドキュメントを参照してください。
うまくいったコード例
created()
メソッド内で、watch
を用いることで、
stateデータ
が変更された瞬間を監視して、stateデータ
を取得します。
これで、state
内のデータを呼び出し、名前情報を取得することができました。
import { mapGetters } from "vuex";
const headerComp = {
template:
`
<div class="comp-header">
<div class="lay-header">
<div class="lay-header-right">
<ul>
// 山田 太郎
<li> {{this.user.first_name}} {{this.user.last_name}}</li>
</ul>
</div>
</div>
</div>
`,
data(){
return {
user: {},
}
},
// ここを変更
created() {
this.$watch(
(state) => state.authUser,
(authUser) => {
this.user = Object.assign({}, authUser); // コピー
}
);
},
computed: {
...mapGetters(["authUser"]),
},
}
export default headerComp
備考 階層を変えるとうまくいく
mapGettersでstateを読み込むファイルの階層を一つ落とすとうまく読み込むことができる。
application.js
でheader_comp.js
を読み込む際には、前述したwatch
を使った。
しかし、階層が一つ下のprofile/index.js
を読み込む際には、watch
を使わずとも、
mapGetters
でstate
にアクセスすることができた。
import * as Vue from "vue";
import headerComp from "./feature/header_comp"
import router from "./../router/index"
import Vuex from './../store/index'
const App = Vue.createApp({
template: `
<headerComp/>
<router-view/>
`,
data() {
return {
}
},
components: {
headerComp
},
});
App.use(router);
App.use(Vuex);
App.mount("#app");
ファイルの読み込み順などの関係で、
header_comp
ではstate
が一瞬空で表示されているのでしょうか?
詳しい原因はよく分かっていません。
最後に
ここまで見てくださりありがとうございます。
今回はstoreのデータ参照のはまりポイントについて紹介いたしました。
Vue.jsの理解はまだまだ浅いので、これからもキャッチアップを続けていきたいと思います。