TL;DR, Define your mutation constant together with the function definition, and export both. Allows co-locating the constant with the function so you can cmd+click
from inside a component, and see the implementation immediately.
Going by the Vuex documentation, to avoid string literals
this.$store.commit('setLoading', false)
is to define your constants in a separate file
mutation-types.js
export const SET_LOADING = 'setLoading'
MyComponent.vue
import { SET_LOADING } from './mutation-types'
this.$store.commit(SET_LOADING, false);
store.js
const store = new Vuex.Store({
//...
mutations: {
[SET_LOADING] (state, val) {
state.loading = val;
}
}
})
Anyone that has ever done this will realize pretty soon that you've lost the ability to cmd+click
to see the function definition! What you end up at is at
export const SET_LOADING = 'setLoading'
And then usually selecting SET_LOADING
, and doing a search in your IDE to get to your store. π©
I've been writing Vue for around 4 years, and React daily for the last 2. If you've ever written action creators, for a React reducer, this should seem pretty familiar.
A better way would be to define your mutation in a separate file
mutations.js
export const SET_LOADING = 'setLoading'
export const setLoading = (state, val) => {
state.loading = val;
}
You still have to make sure setLoading
is the actual function name here, but now it's even easier to spot a typo!
And in your store you'd import both the constant and the function.
store.js
import {SET_LOADING, setLoading} from './mutations.js'
const store = new Vuex.Store({
mutations: {
[SET_LOADING]: setLoading
}
})
Cool! Now you can avoid typing strings, and still cmd+click
to see the mutation definition!
π°Bonus points
Storing the mutations in a separate file also allows breaking up the mutations according to usage, so user mutations can live at: src/user/state/mutations.js
π°π°Extra bonus points
If you've ever googled 'How to call a mutation from another mutation', you'll probably end up creating an action to dispatch multiple mutations. While this is perfectly valid, there are some cases where combining mutations makes more sense.
Now that all your mutations are just separate functions, you can just import and call other mutations as you like.
Combining multiple mutations will make them harder to debug. Consider why you're doing this, and whether you care if they happen in separate steps (use an action), or not (go ahead and combine them).
export const MOVE_ITEM = 'moveItem'
function moveItem(state, {oldIndex, newIndex}) {
const item = state.items[oldIndex]
deleteItem(state, oldIndex)
addItem(state, {item, index: newIndex})
/*
* This is purely an example, actual implementation
* might need you to figure out the modified indexes.
*/
}
function addItem(state, {index, item}) {
//...
}
function deleteItem(state, {index, item}) {
//...
}
React vs. β€οΈ Vue
React, and Vue might have some fundamental differences, but approaches in one can inspire cleaner code when working with the other.γ
I've only recently started doing this, but it's made working with Vuex a better experience personally. If you hate this, and think I'm about to crash, and drown in a pile of spaghetti, please let me know asap!
Top comments (1)
When adding an eventListener in JavaScript, the first parameter is a
type
.I understand the intention to keep this approach for redux action or vuex mutation. But the risks of typos are real and difficult to debug. That's why I prefer the "constant" approach and collocalizing the constant is an excellent practice I hadn't thought of.
Thank you for this article.