the industrial

ブログと言うより自分のためのメモ以外の何モノでもないです。でも読んでくださってありがとうございます。

Vuex にて state に( data で定義した)Object を代入すると参照渡しとなり、当該 data の変数を v-model で参照している場合、値を変更するとエラーとなる

こんなエラーが出た。

app.js:580 Error: [vuex] do not mutate vuex store state outside mutation handlers.
    at assert (commons.app.js:19688)
    at Vue.store._vm.$watch.deep (commons.app.js:20364)
    at Watcher.run (commons.app.js:15703)
    at Watcher.update (commons.app.js:15677)
    at Dep.notify (commons.app.js:11878)
    at Object.reactiveSetter [as keyword] (commons.app.js:12203)
    at Proxy.set (commons.app.js:12225)
    at input (index.js:692)
    at invokeWithErrorHandling (commons.app.js:13000)
    at HTMLInputElement.invoker (commons.app.js:13325)

このエラーの原因はなんの変哲もなく、mutations を通さずに Vuex の state を直接更新しようとしたから。

しかし、直接の原因は data に定義した Object を state に代入していたためだった。

mutations を通して代入していたとしても、( data で定義した)Object を代入すると参照渡しとなり、当該 data の変数を v-model で参照しているため値を変更した瞬間上記のエラーとなってしまう。

ざっくりとソースコードに落としてみる。

Page Component

<template>
  <input v-model="searchCondition.keyword" type="text">
  <button type="button" click="executeSearch">検索</button>
</template>

<script>
export default {
  data() {
    return {
      searchCondition: {
        keyword: ''
      }
    }
  },
  methods: {
    async executeSearch() {
      await this.$store.dispatch('SEARCH', this.searchCondition)
    }
  }
}
</script>

State / mutations / actions

export const state = () => ({
  searchCondition: {
    keyword: ''
  }
})

export const mutations = {
  SET_SEARCH_CONDITION(state, payload) {
    state.searchCondition = payload
  }
}

export const actions = {
  async SEARCH({ commit }, payload) {
    const { data } = await this.$axios.get('/search', { params: { ...payload } }).catch(err => { 
      return err.response
    })
    commit('SET_SEARCH_CONDITION', payload) // Objectの参照渡しとなってしまう
  }
}

対策としては、DeepCopy してあげればよいが、まあとりあえず Shallow Copy でお茶を濁しておく。

commit('SET_SEARCH_CONDITION', { ...payload })

もしかしたら参照渡しとか表現などなど間違っているかもしれないので、ツッコミくだしあ。