Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vuex - 'do not mutate vuex store state outside mutation handlers'

I'm trying to initialize my Vuex store from Firestore. The last line of code context.commit('SET_ACTIVITIES', acts) is what creates the error. I don't think I'm mutating the state directly since I'm using an action. What could I be missing?

Here's my Vuex store:

export default new Vuex.Store({
  strict: true,
  state: {
    activities: []
  },
  mutations: {
    SET_ACTIVITIES: (state, activities) => {
      state.activities = activities
    },
  },

  actions: {
    fetchActivities: context => {
      let acts = []
      let ref = db.collection('activities')
      ref.onSnapshot(snapshot => {
        snapshot.docChanges().forEach(change => {
          if(change.type == 'added') {
            acts.push({
              id: change.doc.id,
              name: change.doc.data().name,
              day: change.doc.data().day
            })
          }
        })
      })
      context.commit('SET_ACTIVITIES', acts)
    }
  }

Also, it gives me the error equal to the number of items in Firestore. Why would it do that if I'm only doing one commit?

console:

[Vue warn]: Error in callback for watcher "function () { return this._data.$$state }": "Error: [vuex] do not mutate vuex store state outside mutation handlers."

and

Error: [vuex] do not mutate vuex store state outside mutation handlers.
    at assert (vuex.esm.js?2f62:87)
    at Vue.store._vm.$watch.deep (vuex.esm.js?2f62:763)
    at Watcher.run (vue.runtime.esm.js?2b0e:4562)
    at Watcher.update (vue.runtime.esm.js?2b0e:4536)
    at Dep.notify (vue.runtime.esm.js?2b0e:730)
    at Array.mutator (vue.runtime.esm.js?2b0e:882)
    at eval (store.js?c0d6:36)
    at eval (index.cjs.js?e89a:21411)
    at eval (index.cjs.js?e89a:4904)
    at LLRBNode.inorderTraversal (index.cjs.js?e89a:1899)
like image 538
tazboy Avatar asked May 06 '19 22:05

tazboy


People also ask

Do not mutate Vuex store state outside mutation handlers object?

Error: [vuex] do not mutate vuex store state outside mutation handlers. The error means what it says. The solution is to mutate the store only with actions/mutations. You shallowly copy the array so it becomes a part of the store, then you continue to mutate it.

What is strict mode in Vuex?

In strict mode, whenever Vuex state is mutated outside of mutation handlers, an error will be thrown. This ensures that all state mutations can be explicitly tracked by debugging tools.

Are Vuex mutation async?

In Vuex, mutations are synchronous transactions: store.

What is the difference between Vuex mutations and actions?

Mutations are intended to receive input only via their payload and to not produce side effects elsewhere. While actions get a full context to work with, mutations only have the state and the payload .


1 Answers

You're running into an issue with object references and asynchronous methods.

CollectionReference#onSnapshot() is asynchronous, triggering the snapshot listener / observer on QuerySnapshot events.

Basically what happens in your code is that you assign the empty array acts to state.activities (same object reference) in your mutation and then, at a later time in your snapshot event handler, directly push elements into it.

A quick solution would be to commit the mutation within the onSnapshot observer

fetchActivities: context => {
  let ref = db.collection('activities')
  ref.onSnapshot(snapshot => {
    let acts = []
    snapshot.docChanges().forEach(change => {
      if(change.type == 'added') {
        acts.push({
          id: change.doc.id,
          name: change.doc.data().name,
          day: change.doc.data().day
        })
      }
    })
    context.commit('SET_ACTIVITIES', acts)
  })
}

If you only want to do an initial fetch of your collection data, use CollectionReference#get() instead. Given it returns a promise, you can use this to make your action composable

async fetchActivities ({ commit }) {
  let snapshot = await db.collection('activities').get()
  let acts = snapshot.docChanges().filter(({ type }) => type === 'added')
      .map(({ doc }) => ({
        id: doc.id,
        name: doc.data().name,
        day: doc.data().day
      }))
  commit('SET_ACTIVITIES', acts)
}
like image 170
Phil Avatar answered Nov 15 '22 05:11

Phil