LoginSignup
12
3

More than 5 years have passed since last update.

【Vuex】変更が検知されないときの注意点

Posted at

Vuexで管理しているデータを変更しても検知されず、DOMに反映されなくて困ったのでメモ。

やりたいこと

  • カードが開いているか閉じているかのフラグ(isOpen)をstoreで管理
  • isOpenがtrueになったら、実際にカードを開く

状況

カードがOpenされているかのフラグをstoreで管理

store.js
actionsPayload.openCard = {
  groupKey: {
    type: Number,
    required: true
  }
};

actions.openCard = (store, payload) => {
  store.commit('openCard', payload);
};

mutationsPayload.openCard = {
  groupKey: {
    type: Number,
    required: true
  }
};
mutations.openCard = (state, payload) => {
  // 一度全部閉める
  if (state.cards.items === null || state.cards.items === undefined) return;
  state.cards.items = state.cards.items.map((card) => {
    card['isOpen'] = false;
    return card;
  });

  // 一個開ける
  Vue.set(state.cards.items[payload.groupKey], 'isOpen', true);
};

カード見出しをクリックしたら詳細を表示する

:v-if="card.isOpen" がtrueになったらカード詳細表示されてほしい・・

card.vue
<template>
  <div>
    <div :v-if="!card.isOpen" @click="handleClick">カード見出し</div>
    <div :v-if="card.isOpen">カード詳細</div>
  </div>
</template>

<script>
export default {
  props: {
    card: {
      type: Object,
      required: true
    },
    groupKey: {
      type: Number,
      required: true
    }
  },
  methods: {
    async handleClick() {
      await this.$store.dispatch('store/openCard', { groupKey: this.groupKey });
    }
  }
};
</script>

困ったこと

上記のコードだとカード詳細はオープンしません。
スクリーンショット 2019-03-12 2.23.55.png
vue devtoolsで確認して、isOpen: true になってるのになぜ😢

解決法

Objectからドット表記でisOpenにアクセスせず、直接propsに渡してあげる

card.vue
<template>
  <div>
    <!-- card.isOpenをpropsのisOpenに変更 -->
    <div :v-if="!isOpen" @click="handleClick">カード見出し</div>
    <div :v-if="isOpen">カード詳細</div>
  </div>
</template>

<script>
export default {
  // 略
  props: {
    // 追加
    isOpen: {
      type: Boolean,
      default: false
    }
  }
};
</script>

vueはObjectの中身の一部が変更しても検知してくれないみたいです。
(実際に詰まったのは、多次元配列だったので、このサンプルのように1次元配列だと変更を検知してくれる場合もあるかも?)

実際に公式でもこう書いてあります。

Vue.js はプロパティの追加または削除を検出できません。
リアクティブの探求 — Vue.js

どうしても変更が検知されない場合の奥の手

vm.$forceUpdate()
API — Vue.js

forceUpdate()すれば強制的に初期化され、追加したデータがリアクティブにDOMに反映されます。
ただ、初期化することで全てのデータが取り直しとなり、あまり良い処理ではありません・・。

もし Vue で強制更新をする必要な場面に遭遇する場合、99.99% のケースであなたは何かを間違えています。
しかし、もし上記の可能性を排除し、手動で強制更新をする非常に稀な状況と認識しているならば、$forceUpdate を用いることで強制更新をすることができます。
特別な問題に対処する — Vue.js

12
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
3