Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use vuex getter after async api call

What is the proper way to call a getter in vuex after you dispatched an async action which mutated the state?

I created an example snippet to illustrate what I mean. As you can see, getLastNameByName() fails because state.persons is empty. The weird thing is, that if I print state.persons in that getter, it prints the array after api call.

Expected behaviour is that getLastNameByName('John') returns {name: 'John', lastname: 'Smith'}

const store = new Vuex.Store({
  state: {
    persons: []
  },

  getters: {
    getLastNameByName: (state) => (name) => {

      // console.log(state.persons) returns the state, yet I cannot call .find on it 
      return state.persons.find(element => {
        return element.name === name
      }).lastname
    },
  },

  mutations: {
    setPersons: (state, payload) => {
      state.persons = [...payload]
    }
  },

  actions: {
    async getPeople({commit}) {
        return new Promise(function(resolve, reject) {
          setTimeout(async () => {
             commit('setPersons', [{
               name: 'John',
               lastname: 'Smith'
            }, {
            name: 'Sarah',
            account: 'Appleseed'
          }])

           resolve();
         }, 1000)
      })
  }
  }
})

new Vue({
  store,
  el: '#app',
  mounted() {
    this.$store.dispatch('getPeople').then( () =>  { 
      console.log(this.$store.getters.getLastNameByName('John'))
    })
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex"></script>

<div id="app">
</div>
like image 369
Frank Avatar asked Dec 14 '18 09:12

Frank


People also ask

Can Vuex actions be async?

To use async and await with Vuex dispatch, we can add an action method that's an async function. const store = new Vuex.

How do you access getters in mutations Vuex?

To access getters within Vuex mutations with Vue. js, we just use the state in the getter and call the getter with the state . //... const store = new Vuex.

Are the results of a Vuex getter cached?

Vuex allows us to define "getters" in the store. You can think of them as computed properties for stores. As of Vue 3.0, the getter's result is not cached as the computed property does.


Video Answer


2 Answers

setTimeout() don't returns awaitable object. Check with promise:

const store = new Vuex.Store({
  state: {
    persons: []
  },

  getters: {
    getLastNameByName: (state) => (name) => {
      return state.persons.find(element => {
        return element.name === name
      }).lastname
    },
  },

  mutations: {
    setPersons: (state, payload) => {
      state.persons = [...payload]
    }
  },

  actions: {
    async getPeople({commit}) {
        return new Promise(function(resolve, reject) {
          setTimeout(async () => {
             commit('setPersons', [{
               name: 'John',
               lastname: 'Smith'
            }, {
            name: 'Sarah',
            account: 'Appleseed'
          }])

           resolve();
         }, 1000)
      })
    }
  }
})

new Vue({
  store,
  el: '#app',
  mounted() {
    this.$store.dispatch('getPeople').then(() => {
       console.log(this.$store.getters.getLastNameByName('John'));
    })
  } 
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex"></script>

<div id="app">
</div>

Anyway direct handling async calls to store isn't a proper way. I think in this case better solutions is to watch store state or use computed properties.

like image 99
YD1m Avatar answered Oct 13 '22 16:10

YD1m


Tried on jsbin.com with few improvements and no problem:

<!DOCTYPE html>

<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Vue example</title>

<div id="app">
  <show-person
    :name="getLastName('John')"
  ></show-person>
</div>

<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vuex.min.js"></script>

<script>
const store = new Vuex.Store({
  state: {
    persons: []
  },

  getters: {
    getLastName: state => name => {
      return state.persons.length
        ? state.persons.find(element => {
            return element.name === name
          }).lastname
        : ''
    }
  },

  mutations: {
    setPersons: (state, payload) => {
      state.persons = [...payload]
    }
  },

  actions: {
    getPeople: ({ commit }) => new Promise(res => {
      const data = [
        {name: 'John', lastname: 'Smith'},
        {name: 'Sarah', account: 'Appleseed'}
      ]
      setTimeout(() => {
        commit('setPersons', data)
        res()
      }, 1000)
    })
  }
})

const ShowPerson = {
  functional: true,
  render: (h, ctx) => h('p', ctx.props.name)
}

new Vue({
  store,
  el: '#app',

  components: {
    ShowPerson
  },

  computed: {
    ...Vuex.mapGetters([
      'getLastName'
    ])
  },

  methods: {
    ...Vuex.mapActions([
      'getPeople'
    ])
  },

  created () {
    this.getPeople()
  }
})
</script>
like image 45
Vladislav Ladicky Avatar answered Oct 13 '22 15:10

Vladislav Ladicky