John Au-Yeung I'm a web developer interested in JavaScript stuff.

Using Vuex 4 with Vue 3

5 min read 1652

Using Vuex 4 With Vue 3

Vuex is the preferred state management solution for Vue apps, and Vuex 4 is the version compatible with Vue 3. Let’s explore how to use Vuex 4 with Vue 3.

Installation

We can install Vuex with Vue 3 in a few ways, one of which is to use the script tag.

To use it, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vuex@4.0.0-beta.4/dist/vuex.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <button @click="increment">increment</button>
      <p>{{count}}</p>
    </div>
    <script>
      const store = new Vuex.Store({
        state: {
          count: 0
        },
        mutations: {
          increment(state) {
            state.count++;
          }
        }
      });

      const app = Vue.createApp({
        methods: {
          increment() {
            this.$store.commit("increment");
          }
        },
        computed: {
          count() {
            return this.$store.state.count;
          }
        }
      });
      app.use(store);
      app.mount("#app");
    </script>
  </body>
</html>

Above, we add the scripts for Vuex 4 and Vue, and then we can use the Vuex global object in our code.

To create a store, we call the Vuex.Store constructor with an object with the states and mutations that we want to add to create a basic store. States are properties that stores data in our Vuex store; they let us access the data from anywhere in our Vue 3 app. Mutations are functions that lets us modify states in the Vuex store.

Once we’ve created our store, we pass it into the Vue.createApp method to add the store to our app. The app.use(store); method call lets us use the store in our Vue 3 app.

Now that the store has been added, we can use the this.$store property to get the states and manipulate our store. The this.$store.commit method lets us commit the mutations to the store.

We commit the increment mutation in our store to update the count state. Therefore, when we click the increment button, the count Vuex state will be updated along with the count computed property.

Getters

To add store states into our app more easily, we can use getters. Getters are functions that returns a state, or states that have been operated on or combined with other values.

For example, we can add a getter by writing:

We made a custom demo for .
No really. Click here to check it out.

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vuex@4.0.0-beta.4/dist/vuex.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <button @click="increment">increment</button>
      <p>{{doubleCount}}</p>
    </div>
    <script>
      const store = new Vuex.Store({
        state: {
          count: 0
        },
        mutations: {
          increment(state) {
            state.count++;
          }
        },
        getters: {
          doubleCount: (state) => {
            return state.count * 2;
          }
        }
      });

      const app = Vue.createApp({
        methods: {
          increment() {
            this.$store.commit("increment");
          }
        },
        computed: {
          ...Vuex.mapGetters(["doubleCount"])
        }
      });
      app.use(store);
      app.mount("#app");
    </script>
  </body>
</html>

We added the doubleCount getter, which returns the count state multiplied by two. Then, in the component, we call the Vuex.mapGetters method with the name of the getter to map it to a computed property. We also have it in the template so we can see its value.

If we want a method as a getter, we can return a function in our getter. For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vuex@4.0.0-beta.4/dist/vuex.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <div>
        <p>{{getTodoById(1).text}}</p>
      </div>
    </div>
    <script>
      const store = new Vuex.Store({
        state: {
          todos: [
            { id: 1, text: "drink", done: true },
            { id: 2, text: "sleep", done: false }
          ]
        },
        getters: {
          getTodoById: (state) => (id) => {
            return state.todos.find((todo) => todo.id === id);
          }
        }
      });

      const app = Vue.createApp({
        computed: {
          ...Vuex.mapGetters(["getTodoById"])
        }
      });
      app.use(store);
      app.mount("#app");
    </script>
  </body>
</html>

We have the todos Vuex store state, and we want to get an entry from it by its id property value. To do that, we have the getTodosById getter method, which returns a function. The function subsequently returns the entry from the state.todos array by calling find to get the value by its id value.

In the component, we call Vuex.mapGetters the same way to map the method to a computed property.Then we can call the function it returns to get the to-do item by its id value. Therefore, 'drink' should be displayed on the browser screen since this has id: 1.

Mutations

We’ve already seen a mutation in the previous examples; it’s just a method we can use to modify a state.

A mutation method can take a payload, which can be used to modify a state value. For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vuex@4.0.0-beta.4/dist/vuex.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <button @click="increment">increment</button>
      <p>{{count}}</p>
    </div>
    <script>
      const store = new Vuex.Store({
        state: {
          count: 0
        },
        mutations: {
          increment(state, n) {
            state.count += n;
          }
        }
      });
      const app = Vue.createApp({
        methods: {
          increment() {
            this.$store.commit("increment", 5);
          }
        },
        computed: {
          count() {
            return this.$store.state.count;
          }
        }
      });
      app.use(store);
      app.mount("#app");
    </script>
  </body>
</html>

Our increment mutation method has an n parameter that is used to increase the value of the count state. Then, we call the this.$store.commit method with a second argument to pass in the value to the increment method. n should now be 5, so the count Vuex state would be incremented by five.

Object style mutation commits

We can pass in an object to the this.$store.commit method. For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vuex@4.0.0-beta.4/dist/vuex.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <button @click="increment">increment</button>
      <p>{{count}}</p>
    </div>
    <script>
      const store = new Vuex.Store({
        state: {
          count: 0
        },
        mutations: {
          increment(state, { amount }) {
            state.count += amount;
          }
        }
      });
      const app = Vue.createApp({
        methods: {
          increment() {
            this.$store.commit({
              type: "increment",
              amount: 5
            });
          }
        },
        computed: {
          count() {
            return this.$store.state.count;
          }
        }
      });
      app.use(store);
      app.mount("#app");
    </script>
  </body>
</html>

We called this.$store.commit with an object with the type and amount properties.

The type property is used to find the name of the mutation method to call. So, we’ll call the increment mutation method since that’s the value of type. The other properties will be passed in with the object that we pass as the second argument of the increment method.

So we get the amount property from the second parameter of increment and use that to update the Vuex store’s count state.

Actions

Mutations do have some limitations. Mutation methods must be synchronous so that their order of execution can be tracked with Vuex. However, Vuex has action methods that let us run mutations to modify a state.

Actions can run any kind of code, including async ones. For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vuex@4.0.0-beta.4/dist/vuex.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <p>{{answer}}</p>
    </div>
    <script>
      const store = new Vuex.Store({
        state: {
          answer: ""
        },
        mutations: {
          setAnswer(state, answer) {
            state.answer = answer;
          }
        },
        actions: {
          async getAnswer(context) {
            const res = await fetch("https://yesno.wtf/api");
            const answer = await res.json();
            context.commit("setAnswer", answer);
          }
        }
      });
      const app = Vue.createApp({
        mounted() {
          this.$store.dispatch("getAnswer");
        },
        computed: {
          answer() {
            return this.$store.state.answer;
          }
        }
      });
      app.use(store);
      app.mount("#app");
    </script>
  </body>
</html>

This will add the setAnswer action, which is added as a method of the actions property. The context parameter has the commit method that lets us commit mutations. The argument is the name of the mutation.

The getAnswer action method is async, and it calls the context.commit method to commit the setAnswer mutation. The answer in the second argument is passed into the setAnswer method as the value of the answer parameter, and it’s set as the value of the state.answer property.

Then, in the component, we can get the answer by using the this.$store.state.answer property. In the mounted hook, we call this.$store.dispatch("getAnswer"); to dispatch the getAnswer action. Therefore, we should see an object with the answer in the template.

Conclusion

Vuex 4 is not terribly different from previous versions of Vuex; the v4 update is mainly for compatibility purposes with Vue 3.

It has the same parts, like getters, mutations, and actions, which are used to get and set Vuex store states.

Experience your Vue apps exactly how a user does

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps - .

John Au-Yeung I'm a web developer interested in JavaScript stuff.

Leave a Reply