Some Helpful Vuex Tips

VueX is a helpful tool you can use to turn your "vue enhanced" websites into fully featured SPAs (Single Page Applications). I've used VueX on a couple of projects now and have learned a few tips to help my workflow that i'll share with you today

Namespace Your Modules

By design, vuex is a single central data store for all parts of your application. All data exists in the same namespace meaning variables from one module can and will overwrite the state variables of another. This works well enough for smaller apps, but as you application grows, you will quickly start stepping on your own toes as different jobs in your app start overlapping. To avoid this, make sure to namespace your modules. Namespacing allows you to modularize your vuex data, separating it into logical sub-sections for each "job" you want vuex to manage for you. Each module receives its own reactive state, getters, and actions making it almost standalone but it's worth noting that any module can still access data and functions from any other. It's up to you to make sure that your vuex modules don't become a complete spaghetti dinner.

Don't access state or mutations directly

Vuex allows you to access/modify module state data in your regular vue components using the mapState function. However, in my experience, direct state data is better off being treated like private variables of a class in an object oriented language, meaning you don't ever access these variables directly. Instead, only access and modify state variables using vuex getters and actions. This adds a bit of complexity to the vuex side of things, but it keeps things a bit simpler on the normal Vue side of the equation.

mapGetters can return functions, too

In addition to returning reactive state data, you can also use mapGetters to return reactive functions that will automatically re run upon any change.

As an example let's say you need to search through an array of user objects to find a user with a specific ID. You could just write a function to do this that you run when necessary, but because Vuex getters map to computed functions within your components, the same reactivity rules that apply to component data and props will also apply to functions.

export default {
    namespaced: true,

    state: () => ({
        // A list of users pulled from the server
        users: []
    }),

    getters: {
        /**
         * Note that we are returning a function here that we can run later. This function has the same 
         * reactive properties as a normal vue computed function so that when the state.users variable
         * changes, the "findUser" function will automatically re run, and return a new function.
         * Since this function is just a normal piece of reactive data, it will also force the 
         * computed property we mapped this findUser function to in our component to re run.
         * 
         * A bit confusing but once you get it your mind will blow.
         */
        xFindUser(state) {
            return (ID) => {
                return (state?.users).find((item) => {
                    return item.id === ID;
                };
            };
        }
    },

    actions: {
        fetchUsers() {
            // do some async thing that fetches users here. When you commit these users to module state, the xFindUser
            // getter will automatically re run, which will also cause the mappedGetter in your component to re run
        }
    }
}
<template>
  <pre>{{ currentUser }}</pre>  
</template>

export default {
    props: {
        // contains the ID of the user we want to find
        primarykey: {
            type: Number,
            default: null
        }
    },

    computed: {
        ...mapActions("user", ['xFindUser']),

      /**
       * This computed function has two pieces of reactive data that will cause it to re-run. 
       * The first is the :primaryKey prop variable. If this prop changes the computed function
       * will re run just like you would expect. The other piece of reactive data is the
       * findUser function itself. This function is re generated each time the users
       * state varaible of our vueX module changes, meaning as soon as users are pulled from the 
       * server, this function will automatically re run and return the user with ID :primaryKey
       * 
       * Once this clicks your mind will explode.
       */
      currentUser() {
          return this.xFindUser(this.primarykey);
      }
    }
}

Prepend all getters and actions in vuex with "x"

As shown above, the vuex methods "mapGetters" and "mapActions" can be used to copy vuex module methods and data into your regular vue components. This data can be accessed and modified as if it was a normal piece of data within your vue component.

This is a very handy pattern that is simple to understand in smaller applications, but one thing I have found is that in really large vue/vuex applications, it can get really confusing which data/methods belongs to the vue component itself, and which data belongs to a vuex module, making it difficult to debug issues that pop up.

Thankfully there's an easy solution to this. Just prepend all of your vuex getters and actions with "x" so that when vuex data/methods are mapped into your component, they will be immediately recognizeable without understanding all of the logic found inside of a component.

Let's say you have a vuex module called "User". User has an action called "fetchUser" that pulls user data down from the server and commits it to module state. Rename that action to "xFetchUser" (i.e exactly the same as before except prepended with an 'x') so that when this method has been "mapAction"'d into your vue component, it's immediately obvious that this method can be found in Vuex, saving yourself some brain power that can now be applied elsewhere.

Validate data in your mutations

Vuex requires all changes to module state to happen via special functions called mutations. This means that in order to modify any data in the state of your vuex module, you must write a specialized mutation to do so. Use these mutations as an opportunity to keep your module's private data in a valid state (meaning a state in which the application will not crash when the module state data is consumed by our application). Validate that all changes to module state are legit BEFORE committing this data to your module state. If the data doesn't pass whatever validation rules you decide, consider throwing an exception to halt your application. It's better to fail on purpose in a known bad state then to silently fail later and not know the reason why it happened.

Similar Blog Posts

A Simple Trait Validation Technique

I show off a common Trait issue and what I do to work around it

Batch Converting PNG files to WEBP

A script I made to convert png files to webp

Converting Legacy PHP to Laravel

The strategy I used to rewrite an entire legacy PHP application in Laravel by myself