Question 166
How do you perform asynchronous operations?
In Vuex, mutations are synchronous transactions. But if you want to handle asynchronous operations then you should use actions.
Question 166
In Vuex, mutations are synchronous transactions. But if you want to handle asynchronous operations then you should use actions.
Question 167
Actions are similar to mutations, but there are two main differences,
Question 168
Vuex provides actions property similar mutations property in order to define action handlers. These action handlers receive context object as an argument which has same properties and methods of store instance.
Let's see counter example to demonstrate increment action which commits respective mutation,
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Question 169
Actions are simply triggered with the store.dispatch method as below,
store.dispatch('increment')
Question 170
Yes, actions support both payload and object style format similar to mutations.
// dispatch with a payload
store.dispatch('incrementAsync', {
amount: 10
})
// dispatch with an object
store.dispatch({
type: 'incrementAsync',
amount: 10
})
Question 171
Styled components is a CSS-in-JS library used mainly for ReactJS applications. If you want to use it for VueJS applications, there is a VueJS styled components library port available, however, it is not a common practice.
Question 172
You can dispatch actions in components with this.$store.dispatch('action name'), or use the mapActions helper which maps component methods to store.dispatch calls.
For example, you can dispatch increment actions in counter component as below,
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // map `this.increment()` to `this.$store.dispatch('increment')`
// `mapActions` also supports payloads:
'incrementBy' // map `this.incrementBy(amount)` to `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // map `this.add()` to `this.$store.dispatch('increment')`
})
}
}
Question 173
You can write multiple actions together to handle more complex async flows either by chaining promises or
async/await. i.e, store.dispatch can handle Promise returned by the triggered action handler and it also returns
Promise.
Let's take two actions to see how they are combined and handled async flows,
actions: {
actionOne ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('first mutation')
resolve()
}, 1000)
})
},
actionTwo ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('second mutation')
})
}
}
As per the above example, When you try to dispatch actionTwo it dispatches actionOne first and then commits respective mutation. You can still simplify with async/await as below,
actions: {
async actionOne ({ commit }) {
commit('first mutation', await getDataAsPromise())
},
async actionTwo ({ dispatch, commit }) {
await dispatch('actionOne') // wait for `actionA` to finish
commit('second mutation', await getSomeDataAsPromise())
}
}
Question 174
If you keep all state of our application in a single big state, the store can get really bloated. To solve this problem, Vuex allows us to divide our store into modules. Here, each module can contain its own state, mutations, actions, getters, and even nested modules.
Let's take an example with multiple modules, configuring them in vuex and accessing different modules,
const moduleOne = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleTwo = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const store = new Vuex.Store({
modules: {
one: moduleOne,
two: moduleTwo
}
})
store.state.one // -> `moduleOne's state
store.state.two // -> `moduleTwo's state
Question 175
When you use modules the local state will be available to mutations, getters and actions in different ways.
const moduleOne = {
state: { count: 0 },
mutations: {
increment (state) {
state.count++; // Here state refers local module state
}
},
getters: {
average (state) {
return state.count / 2
}
}
}
const moduleOne = {
actions: {
incrementConditional ({ state, commit, rootState }) {
if (state.count < rootState.count) {
commit('increment')
}
}
}
}
Question 176
By default, actions, mutations and getters inside modules are still registered under the global namespace. Because of that multiple modules react to the same mutation/action type.
Question 177
Sometime you may need to create multiple instances of a module.
For example, it is needed in the below cases,
In those cases, you need to assign to a variable and export it for reusability,
const MyReusableModule = {
// state
// mutations, actions, getters...
}
Question 178
Vuex enforces below high-level principles,
Question 179
In strict mode, you can't mutate state directly using v-model attribute. If you use v-model it throws an error
because mutation is not performed inside an explicit Vuex mutation handler.
For example, the below input throws an error due to v-model usage
<input v-model="stateObject.message">
In this case, you need to bind the <input>'s value. It can be resolved using value attribute as below,
<input :value="username" @input="updateProfile">
computed: {
...mapState({
username: state => state.user.username
})
},
methods: {
updateProfile (e) {
this.$store.commit('updateProfile', e.target.value)
}
},
mutations: {
updateProfile (state, username) {
state.user.username = username
}
}
Question 180
You can still use model directive using two-way computed property with a setter.
<input v-model="username">
computed: {
username: {
get () {
return this.$store.state.user.username
},
set (value) {
this.$store.commit('updateProfile', value)
}
}
}
mutations: {
updateProfile (state, username) {
state.user.username = username
}
}