Vuex With Class Components
Tags: vue, vuex, typescript
Published: 2021-11-22 16:47:56

Helloooo, in this post I'll show you how you can use [vuex](https://vuex.vuejs.org) with **TypeScript** and [class-components](https://class-component.vuejs.org).

# Disclaimer

In this tutorial I will be using:

- Vue 2
- Vuex ^3.6.2
- TypeScript 4.5

# What we're gonna build
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iiihdzlvijdwx43e9jqe.gif)

# Creating the project

Now let's start coding! First we have to create our Vue.js app. To do that run:

```ts
vue create vuex-counter
```

and make sure you include `Vuex`, `TypeScript` and `Use class components` in your options.

![](https://i.imgur.com/X9kphe6.png)

![](https://i.imgur.com/an0rvor.png)

![](https://i.imgur.com/S3R6Pug.png)

![](https://i.imgur.com/Gm322CF.png)

# Creating the store

Let's now create the Vuex store. The store will consist of a singular state which will contain the main count from where we'll derive the incremented and decremented ones.

`src/store/index.ts`

```ts
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    count: 1,
  },
  getters: {},
  mutations: {},
  actions: {},
  modules: {},
});
```

## Getters

Using the `count` variable in the state we will use getters to fetch the current count, the incremented count and the decremented count. Before we do that though, we'll first create a type for our state so that
we can explicitly type out the arguments required for our getters.

`src/types.ts`

```ts
export interface StateType {
  count: number;
}
```

`src/store/index.ts`
Now we can use this type to create our getters.

```ts
import Vue from "vue";
import Vuex from "vuex";
import { StateType } from "@/types";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    count: 1,
  },
  getters: {
    currentCount(state: StateType): number {
      return state.count;
    },
    previousCount(state: StateType): number {
      return state.count - 1;
    },
    nextCount(state: StateType): number {
      return state.count + 1;
    },
  },
  mutations: {},
  actions: {},
  modules: {},
});
```

## Mutations and Actions

Now let's create some simple mutations to mutate the `count` variable of the state. This will cause
`nextCount` and `previousCount` to update accordingly.

`src/store/index.ts`

```ts
import Vue from "vue";
import Vuex from "vuex";
import { StateType } from "@/types";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    count: 1,
  },
  getters: {
    currentCount(state: StateType): number {
      return state.count;
    },
    previousCount(state: StateType): number {
      return state.count - 1;
    },
    nextCount(state: StateType): number {
      return state.count + 1;
    },
  },
  mutations: {
    increment(state: StateType): void {
      state.count++;
    },
    decrement(state: StateType): void {
      state.count--;
    },
  },
  actions: {},
  modules: {},
});
```

Here we are returning `void` because apart from mutating the `count` value we are not returning anything.
Of course, now we need to run these mutations so lets create some actions for that.

`src/store/index.ts`

```ts
import Vue from "vue";
import Vuex, { ActionContext } from "vuex";
import { StateType } from "@/types";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    count: 1,
  },
  getters: {
    currentCount(state: StateType): number {
      return state.count;
    },
    previousCount(state: StateType): number {
      return state.count - 1;
    },
    nextCount(state: StateType): number {
      return state.count + 1;
    },
  },
  mutations: {
    increment(state: StateType): void {
      state.count++;
    },
    decrement(state: StateType): void {
      state.count--;
    },
  },
  actions: {
    increment(ctx: ActionContext<StateType, StateType>): void {
      ctx.commit("increment");
    },
    decrement(ctx: ActionContext<StateType, StateType>): void {
      ctx.commit("decrement");
    },
  },
  modules: {},
});
```

Alrighty, now we're done with the store and we can move onto using these little bits of state in our UI!

# Using the store in our component

I have a created a component called `Counter` and set it up like this:

```vue
<template>
  <div>
    <h1>vue counter</h1>
    <span>
      <button>&lt; 0</button>
      1
      <button>&gt; 2</button>
    </span>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from "vue-property-decorator";

@Component
export default class Counter extends Vue {}
</script>

<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>
```

Now normally to access our store we would so something like:

```ts
this.$store.count; // etc..
```

But Vuex's TypeScript support is kinda jank and it doesn't work well with class components. So we will have to add a library called `vuex-class` to use our store in our component.

```bash
yarn add vuex-class
```

**or**

```bash
npm install vuex-class
```

So the way `vuex-class` works is you have an associated decorator for a getter, mutation etc. and we pass
that decorator to a variable with the same name as the name of the mutation or getter in the store. For example the way we would call our `currentCount` getter is:

`src/components/Counter.vue`

```vue
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import { Getter } from "vuex-class";

@Component
export default class Counter extends Vue {
  // getters
  @Getter currentCount!: number;
}
</script>
```

And we can call this `currentCount` property in our `template`.

`src/components/Counter.vue`

```vue
<template>
  <div>
    <h1>vue counter</h1>
    <span>
      <button>&lt; 0</button>
      {{ currentCount }}
      <button>&gt; 2</button>
    </span>
  </div>
</template>
```

Now we can do the same for the other getters:

`src/components/Counter.vue`

```vue
<template>
  <div>
    <h1>vue counter</h1>
    <span>
      <button>&lt; {{ previousCount }}</button>
      {{ currentCount }}
      <button>&gt; {{ nextCount }}</button>
    </span>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import { Getter } from "vuex-class";

@Component
export default class Counter extends Vue {
  // getters
  @Getter currentCount!: number;
  @Getter previousCount!: number;
  @Getter nextCount!: number;
}
</script>
```

We can use the same syntax to include our actions using `@Action`. Then we will be able to use it as
the buttons' `@click` handlers.

`src/components/Counter.vue`

```vue
<template>
  <div>
    <h1>vue counter</h1>
    <span>
      <button @click="decrement">&lt; {{ previousCount }}</button>
      {{ currentCount }}
      <button @click="increment">&gt; {{ nextCount }}</button>
    </span>
  </div>
</template>

<script lang="ts">
import { StateType } from "@/types";
import { Component, Vue } from "vue-property-decorator";
import { ActionContext } from "vuex";
import { Getter, Action } from "vuex-class";

@Component
export default class Counter extends Vue {
  // getters
  @Getter currentCount!: number;
  @Getter previousCount!: number;
  @Getter nextCount!: number;

  // actions
  @Action increment!: ActionContext<StateType, StateType>;
  @Action decrement!: ActionContext<StateType, StateType>;
}
</script>
```

And that's it! You can use the same procedure to use them in bigger/more complex stores too! `vuex-class` also has support for modules and you can use them with namespaces.

I'll catch you guys in my next post!