Reputation: 5508
I have a Vuex
store in my application that defines getters
, actions
and mutations
. Now, one action requests data from an API and commits the received data to the mutation that manipulates the state. This particular action (named initConfiguration
) loads some metadata and configurations that must be applied to the store before subsequent actions can be executed. In my root component I have some initialization code in the mounted
lifetime-hook function as follows:
new Vue({
el: '#app',
store,
render: h => h(App),
mounted() {
store.dispatch('initConfiguration').then(() => {
store.dispatch('loadData')
})
}
})
The problem I am facing is that the state has not been changed before the next action loadData
gets called, and thus this action fails miserably due to the missing/uninitialized data. Since there will be several actions that may be called eventually, I´d like to avoid to dispatch the loadData
action right from the initConfiguration
action (and as I learned, the invocation of actions by a mutation must be avoided).
This is my store implementation...
export default Vuex.Store({
state: {
metadata: {
initialized: false,
config: { }
}
},
mutations: {
setConfiguration(state, config) {
state.metadata.config = config
state.metadata.initialized = true
}
},
actions: {
initConfiguration({commit}) {
axios.get('configuration').then((response) => {
commit('setConfiguration', response.data)
})
},
loadData({commit, state}) {
axios.get(state.metadata.config.tenantDataUrl) // crashes here due to undefined data
.then((response) => { ... });
}
}
})
How do I chain actions correctly, and assure that the state gets updated in between?
Upvotes: 3
Views: 1546
Reputation: 2922
You need to return your axios promise. Add a return to each axios call and your existing code will work.
You can also re-write like this
new Vue({
el: '#app',
store,
render: h => h(App),
async mounted() {
await store.dispatch('initConfiguration')
await store.dispatch('loadData')
}
})
.
export default Vuex.Store({
state: {
metadata: {
initialized: false,
config: { }
}
},
mutations: {
setConfiguration(state, config) {
state.metadata.config = config
state.metadata.initialized = true
}
},
actions: {
async initConfiguration({commit}) {
const response = await axios.get('configuration')
commit('setConfiguration', response.data)
},
async loadData({commit, state}) {
const response = await axios.get(state.metadata.config.tenantDataUrl) // crashes here due to undefined data
// do whatever with response, or try/catch
}
}
})
If you need the vue template to update at each step, add this
new Vue({
el: '#app',
store,
render: h => h(App),
async mounted() {
await store.dispatch('initConfiguration')
await this.$nextTick() // cause vue to run getters and update computeds
await store.dispatch('loadData')
}
})
Upvotes: 3