In Vuex, mutations are synchronous transactions. But if you want to handle asynchronous operations then you should use actions.
Vue.js Interview Questions
Actions are similar to mutations, but there are two main differences,
- Mutations perform mutations on the state, actions commit mutations.
- Actions can contain arbitrary asynchronous operations unlike mutations.
Question 168
Give an example usage of actions?
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
How do you dispatch actions?
Actions are simply triggered with the store.dispatch method as below,
store.dispatch('increment')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
Can I use Styled Components in VueJS?
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
How do you dispatch actions in components?
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
How do you compose actions?
You can write multiple actions together to handle more complex async flows either by chaining promises or async/await. i.e,
store.dispatchcan 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
What are modules in vuex?
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 stateQuestion 175
What is module local state?
When you use modules the local state will be available to mutations, getters and actions in different ways.
- Both mutations and getters will receive module local state as first argument.
const moduleOne = { state: { count: 0 }, mutations: { increment (state) { state.count++; // Here state refers local module state } }, getters: { average (state) { return state.count / 2 } } }- In actions, local state will be available as first argument.
const moduleOne = { actions: { incrementConditional ({ state, commit, rootState }) { if (state.count < rootState.count) { commit('increment') } } } }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
When do you reuse modules?
Sometime you may need to create multiple instances of a module.
For example, it is needed in the below cases,
- If multiple stores that use the same module
- Register the same module multiple times in the same store.
In those cases, you need to assign to a variable and export it for reusability,
const MyReusableModule = { // state // mutations, actions, getters... }Question 178
What are the principles enforced by vuex?
Vuex enforces below high-level principles,
- The Application-level state need to be centralized in the store
- The state should be mutated by committing mutations only(i.e, for synchronous transactions)
- The actions should be used for asynchronous transactions.
In strict mode, you can't mutate state directly using
v-modelattribute. 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 } }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 } }