【Vuex】createdでstoreを呼び出してもundefinedになるんだが？

# createdでstoreを呼び出してもundefinedになるんだが？

はい、タイトル通りです。
グローバルで使いたいオブジェクトを`Vuex`の`store`に保存して、
任意のタイミングで呼び出したかったのですが、`undefined`とうまく取り出せませんでした。
その原因と解決策について、備忘録として残したいと思います。


# やりたいこと
`Vuex`の`store`にオブジェクトを保管して、他のコンポーネントでも使用したい。
今回はログインしたユーザーオブジェクトを`store`に保存して、
ユーザー情報を他のコンポーネントでも自由に取り出したい。
名前とか、プロフィール画像とか。

# 前提条件

### storeの設定
`store`は以下のように、ユーザーオブジェクトを`state`にセットしている。
コンポーネント側では`mapGetters`を使って、`state`の値を参照する。

```index.js
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()` のライフサイクルメソッドの後っぽい。



```header_comp.js
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`データが更新されたときにコールバック関数を呼び出すことができます。

```vue.js
watch(fn: Function, callback: Function, options?: Object): Function
```

- 第一引数には、`store`の`stateデータ`を
- 第二引数には、第一引数の返り値を受け取り、適当な処理を
- 第三引数には、オプションを追加することができます。

詳しい説明は[公式ドキュメント](https://vuex.vuejs.org/ja/api/#watch)を参照してください。


# うまくいったコード例
`created()`メソッド内で、`watch`を用いることで、
`stateデータ`が変更された瞬間を監視して、`stateデータ`を取得します。

これで、`state`内のデータを呼び出し、名前情報を取得することができました。

```header_comp.js
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`にアクセスすることができた。

ファイル階層
<img width="400" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2508607/b3518339-bb5f-fb35-edcd-bb47a90cd4d1.png">

```application.js

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の理解はまだまだ浅いので、これからもキャッチアップを続けていきたいと思います。