Vue.js と Vuex を使ってみてハマった点を紹介するだけのブログです。

こんにちは。最近、業務で Vue.js と Vuex を使用する機会があったのでそのときにハマった点について記録しておこうと思います。

Vuexで管理しているグローバルステートをローカルステートにコピーする

1つ目は Vuex で管理しているグローバルステートをローカルにコピーする方法です。まず、なんでせっかく Vuex で管理しているステートのデータをローカルにコピーすることがあるのかという話なのですが、それはそのコンポーネント内でコピー元のデータを変更したり、追加、削除したりといったことを頻繁にしたいが、 Vuex のお作法にしたがって変更するのは少し冗長でめんどくさいといった場面があったためです。 ローカルステートにコピーすれば、そのコンポーネント内で自由にデータを追加、変更、削除することができます。

普通に考えたら、下記のコードのように、created のライフサイクルフックで、Vuex から取得したグローバルステートをローカルステートに代入すればよいかと思います。

import Vue from 'vue'
import { mapState } from 'vuex'

export default {
  name: 'Hoge'
  data() {
    return {
      items: [],
    }
  },
  computed: {
    ...mapState({
      products: state => state.products,
    }),
  },
  created() {
     this.items = this.products;
  }
};

しかし、実際には上記のコードではうまく動作しません。それはなぜかと言うと、created() のライフサイクルメソッドが実行されるときには Vuex 内の products というstateは空だからです。

というのも、この products というステートは非同期でHTTP通信をしてAPI経由で取得している情報のため、Hoge コンポーネントが作成されるときにデータが取得されていることが保証されていないからです。 この問題を解決するためには以下のようなコードを書く必要があります。

import Vue from 'vue'

export default {
  name: 'Hoge'
  data() {
    return {
      items: [],
    }
  },
  created() {
     this.$store.watch(
      (state) => state.products,
      (items) => {
        this.items = JSON.parse(JSON.stringify(items)); // JavaScript でディープコピーするためのハック
      }
    );
  }
};

Vuex のインスタンスメソッドである watch メソッドを使用することで、Vuex のストアデータが更新されたときにコールバック関数を呼び出すことができます。第一引数のコールバック関数には最初の引数としてストアのステートを、2番目の引数としてゲッターを受け取ることができます。第二引数のコールバック関数は第一引数のコールバック関数の返り値を取ることができます。詳しい説明は Vuex 公式ドキュメントを参照してください。

watch メソッドを使用することで、Vuex のストアのステートの変更、つまり、データが取得された瞬間を監視してローカルステートにストアのステートを代入することができます。

Vue でセレクトボックスの初期値を設定できない

HTMLでセレクトボックスの初期値を設定する場合は以下のように、最初の選択肢の value 属性の値を空にするような実装をすることで、セレクトボックスの最初の見た目を「選択してください」といったテキストにすることが多いです。

<select name="hoge" v-model="selected">
  <option value="" selected>選択してください</option>
  <option value="1">1</option>
  <option value="2">2</option>
  <option value="3">3</option>
  <option value="4">4</option>
  <option value="5">5</option>
</select>

しかし、Vue.jsを使用するとき、selected というステートの値をAPI経由で取得する場合に、上記のようにセレクトボックスを実装すると、セレクトボックスの最初の見た目を「選択してください」のテキストにすることができません。これは Vue.js の仕様なのですが、v-model を指定している場合はHTML中の selected 属性や checked 属性は無視されるためです。Vue.js でセレクトボックスやチェックボックスの初期値を設定するためには Vue クラスを初期化する際に指定する必要があります。

例えば、↑ のソースコードで選択してくださいを初期値をにしたい場合は以下のように ステートの初期値として '' 空文字を指定して上げる必要があります。

const app = new Vue({
   el: '#app',
   data: {
       selected: ''
   }
});

また、今回自分がハマったポイントとしては selected のステートの値をAPI経由で取得する場合です。APIの仕様的に値がない場合はプロパティがない状態で JSON データが返却されるという仕様だったため初期値を設定することができませんでした。この場合はフロントエンド側でプロパティがない場合は追加するか、JSON データを返却するバックエンド側で初期値を設定しておくしかありません。

今回の場合はフロントエンドのコードが複雑になりすぎると思ったので、バックエンドのPHP側で初期値を指定することで解決しました。

まとめ

今回は Vue.js を使用する際にハマったポイントを2点紹介しました。 Vue.js に関してはまだまだ初心者で理解が浅いところもあるのでこれからキャッチアップしていきたいと思います。


Related Posts