简体   繁体   中英

How to write Vuex mutation without asynchronous promise

I am building an app with Vue.js, Vuex, and Firebase. In this app, data from Firestore database is updated to the state in mutations. The app in its current form seems to work fine as the state is then returned to the template by way of the getters method. However, it is my understanding that mutations are synchronous and promises (.then) are asynchronous. Therefore, the .then promise should NOT be inside mutations. So why does this app still execute properly? What would be the best way to reconfigure mutations so that FB data is updated to the state without a promise? Or is it even necessary to change this? Here is my Vuex code. Thanks!

import Vue from 'vue'
import Vuex from 'vuex'
import firebase from 'firebase'
import db from '@/firebase/init'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    items: null
  },
  getters: {
    getItems: state => {
      return state.items
    }
  },
  mutations: {
    setAllUser: state => {
      const items = []
      db.collection('users').get()
      .then(snapshot => {
        snapshot.forEach(doc => {
          let userData = doc.data()
          userData.id = doc.id
          items.push(userData)
        })
        state.items = items
      })
    },
  actions: {
    setAllUser: context => {
      context.commit('setAllUser')
    }
  }
})


**SOLUTION**

import Vue from 'vue'
import Vuex from 'vuex'
import firebase from 'firebase';
import db from '@/firebase/init'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    items: null
  },
  getters: {
    getItems: state => {
      return state.items
    }
  },
  mutations: {
    setAllUser: (state, items) => {
      state.items = items
    }
  },
  actions: {
    setAllUser: async context => {
      let snapshot = await db.collection('users').get();
      const items =[]
      snapshot.forEach(doc => {
        let userData = doc.data()
        userData.id = doc.id
        items.push(userData)
      })
      context.commit('setAllUser', items)
    }
  }
})

Move your async code to actions:

{
  mutations: {
    setAllUser: (state, items) => state.items = items
  },
  actions: {
    setAllUser: async context => {
      let snapshot = await db.collection('users').get();
      let items = snapshot.map(doc => {
        let userData = doc.data();
        userData.id = doc.id
        return userData;
      });
      context.commit('setAllUser', items)
    }
}

So for the question why?

  • Single responsibility: your mutation should only be responsible for setting the state.
  • Not sure how would vue-devtools react, I guess it will just show you a single mutation.
  • Your mutation should only depend on payload and state, not on the network/server response etc
  • Your colleagues won't expect this to happen

Why it works?

  • The same reason it works if you just do this directly from devtools: window.store.state.items = [] . Vuex just overrides getters and updating the state fires updating the dom.

PS You can use promise.then inside your action instead of await .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM