DEV Community

Mike Wu
Mike Wu

Posted on • Edited on

Use Vuex? Try this next!

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)

Collapse
 
enguerran profile image
enguerran πŸπŸ’¨

When adding an eventListener in JavaScript, the first parameter is a type.

A case-sensitive string representing the event type to listen for.

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.