Vuex
Similar to Redux, Vuex advocates the single point of truth approach. State is immutabled and, similarly to Redux, should be changed by pure functions
TODO: check if really pure functions or impure is OK
Global access
To avoid importing store in all components, add at the root components:
// in some App.vue > script
import store from "./store";
export default {
name: "app",
// provide the store using the "store" option.
// this will inject the store instance to all child components.
store
};
Store is then access via $store
. To avoid complex mapping, use mapState
:
// in some components > script
export default {
name: "some-component",
computed: {
// option 1: directly fetching from store
productsFromStore() {
return this.$store.state.products;
},
// option 2: use `mapState`
...mapState({
products: state => state.products
})
}
};
Notice that spread syntax is used.
Actions and mutations
Synchronous: use
mutations
directly Asynchronous: useactions
to commitmutations
State changes are triggered by committed mutations:
// In some component method
// this will trigger a synchronous changes
this.$state.commit("addProduct", product);
// Somewhere lese
this.$state.dispatch("asyncAddOrder", order);
// In some store.js
const store = new Vuex.Store({
state:{
orders: [],
products: []
}
actions: {
asyncAddOrder({commit}, order){
placingOrdering(orderin).then((placedOrder) => {
commit('addOrder', placedOrder);
})
},
// OR await FTW:
asyncAddOrder: async ({commit}, order) => {
const placedOrder = await placingOrdering(order);
commit('addOrder', placedOrder);
}
},
mutations: {
addProduct(state, product) {
// do some stuff
}
addOrder(state, order){
// do some stuff
}
}
});
In this example:
addProduct
is done synchronouslyaddOrder
is done asynchronously.destructing
syntax is used for{commit}
instead ofstore
to avoid spammingstore.commit
Getters
Store can also provide getters for computed values (filtering, miscellaneous computing…). Refer to documentation
Modules
Like Redux/Saga, Vuex can be modular with the modules
attribute:
Consider a catalog module:
// src/store/modules/catalog.js
import API from "../api";
const state = {
list: [], // catalogs list
entity: {} // single displayed or edited catalog
};
const actions = {
list_load: async ({ commit }) => {
const catalogs = await API.get("/catalogs");
commit("list_loaded");
}
};
const mutations = {
list_loaded: (state, list) => {
state.list = list;
}
};
export default {
namespaced: true,
state,
actions,
mutations
};
Such module can be imported in the root store:
// src/store/index.js
import Vue from "vue";
import Vuex from "vuex";
import catalogs from "./modules/catalog";
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
catalogs
}
});
export default store;
As namespaced: true
is specified in catalog module, list_load
action is not recognised and has to be prepended with the namespace: catalog/list_load
. Namespace is defined by the name used to import the module.
Sources:
vue state managementMentionned: