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>
To use async and await with Vuex dispatch, we can add an action method that's an async function. const store = new 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.
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.
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.
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With