Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vuex & VueJS (Do not mutate vuex store state outside mutation handlers)

I'm trying to create a listenAuth function that watches "onAuthStateChanged" in firebase to notify the vuex store when a user has logged in or out. As far as I can tell, I'm only modifying state.authData using the mutation handler, unless I'm missing something?

I'm getting the error:

[vuex] Do not mutate vuex store state outside mutation handlers.

Here's my App.vue javascript (from my component)

<script>
// import Navigation from './components/Navigation'
import * as actions from './vuex/actions'
import store from './vuex/store'
import firebase from 'firebase/app'

export default {
  store,
  ready: function () {
    this.listenAuth()
  },
  vuex: {
    actions,
    getters: {
      authData: state => state.authData,
      user: state => state.user
    }
  },
  components: {
    // Navigation
  },
  watch: {
    authData (val) {
      if (!val) {
        this.redirectLogin
        this.$route.router.go('/login')
      }
    }
  },
  methods: {
    listenAuth: function () {
      firebase.auth().onAuthStateChanged((authData) => {
        this.changeAuth(authData)
      })
    }
  }
}
</script>

Here's my action (changeAuth) function

export const changeAuth = ({ dispatch, state }, authData) => {
  dispatch(types.AUTH_CHANGED, authData)
}

Here's my store (the parts that matter)

const mutations = {
  AUTH_CHANGED (state, authData) {
    state.authData = authData
  }
}

const state = {
  authData: {}
}
like image 533
Jayson H Avatar asked Jul 14 '16 03:07

Jayson H


2 Answers

After struggling with the same problem, I found that the error only happens when we try to store the auth/user data in the Vuex state.

Changing from...

const mutations = {
  AUTH_CHANGED (state, authData) {
    state.authData = authData
  }
}

...to...

const mutations = {
  AUTH_CHANGED (state, authData) {
    state.authData = JSON.parse(JSON.stringify(authData))
  }
}

would solve your case.

like image 107
gustavopch Avatar answered Oct 19 '22 06:10

gustavopch


I also came across this issue. My store:

  state: {
    items: []
  },
  mutations: {
    SetItems (state, payload) {
      // Warning
      state.items = payload.items
    }
  },
  actions: {
    FetchItems ({commit, state}, payload) {
      api.getItemsData(payload.sheetID)
        .then(items => commit('SetItems', {items}))
    }
  }

Fixed it by replace state.items = payload.items with:

state.items = payload.items.slice()

The reason is that arrays are stored as references in Javascript and payload.items is likely to be changed outside Vuex. So we should just use a fresh copy of payload.items instead.

For state objects, use:

state.someObj = Object.assign({}, payload.someObj)

And don't use JSON.parse(JSON.stringify(someObj)) as it's much slower.

like image 42
KF Lin Avatar answered Oct 19 '22 07:10

KF Lin