Reputation: 1754
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)
Upvotes: 6
Views: 8907
Reputation: 165062
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)
}
Upvotes: 9