Vuex With Class Components
Dhruva Srinivas
Posted on November 22, 2021
Helloooo, in this post I'll show you how you can use vuex with TypeScript and class-components.
Disclaimer
In this tutorial I will be using:
- Vue 2
- Vuex ^3.6.2
- TypeScript 4.5
What we're gonna build
Creating the project
Now let's start coding! First we have to create our Vue.js app. To do that run:
vue create vuex-counter
and make sure you include Vuex
, TypeScript
and Use class components
in your options.
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
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
export interface StateType {
count: number;
}
src/store/index.ts
Now we can use this type to create our getters.
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
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
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:
<template>
<div>
<h1>vue counter</h1>
<span>
<button>< 0</button>
1
<button>> 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:
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.
yarn add vuex-class
or
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
<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
<template>
<div>
<h1>vue counter</h1>
<span>
<button>< 0</button>
{{ currentCount }}
<button>> 2</button>
</span>
</div>
</template>
Now we can do the same for the other getters:
src/components/Counter.vue
<template>
<div>
<h1>vue counter</h1>
<span>
<button>< {{ previousCount }}</button>
{{ currentCount }}
<button>> {{ 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
<template>
<div>
<h1>vue counter</h1>
<span>
<button @click="decrement">< {{ previousCount }}</button>
{{ currentCount }}
<button @click="increment">> {{ 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!
Posted on November 22, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.