Introduction
Let's build a grocery list application using Vue 3 with Typescript and the Vue store Pinia! I just found out that Edward have tweeted about publishing the documentation for Pinia so I thought I'd share how we can create a simple application using this store.
I will only be covering the very basic implementation of the Pinia store.
Pre-Requisites
This article assumes the basic knowledge and understanding or familiarity with:
- Vue 3 (Composition API)
- TypeScript
- Prior understanding of what State Management is
I will be using TypeScript for this application so I hope you understand at least the basic type annotations. Otherwise let's get right at it and start building this app!
Installation
If you don't have the Vue CLI installed yet then make sure to install it, or if your Vue CLI isn't updated yet then make sure it is on the latest version.
$ npm i -g @vue/cli
If you are on a Linux distro then add sudo
at the beginning of the command since we are installing Vue CLI globally.
And once that is done let's ask Vue CLI to scaffold a Vue 3 project for us. Make sure you have selected Vue 3.
$ vue create vue-3-pinia-grocery-app
And once that is done navigate into the app and open the project in your IDE.
$ cd vue-3-pinia-grocery-app && code .
Then let us add our sugar, TypeScript.
$ vue add typescript
For now these are my selected options, you can choose on your own if you prefer.
Next is to install Pinia as the dependency for this project.
$ npm install pinia@next
And lastly install faker since I am kind of lazy to create forms for this tutorial and creating a form and validating it is sort of an out of scope. So to make things quick, let's just generate some random data from this faker package.
$ npm install faker
$ npm install --save @types/faker
$ npm install uuid
$ npm install --save-dev @types/uuid
Since I plan to use some fake data for quick data generation. Update your model code as I will have a method called generateFakeData()
to generate an Item.
import { v4 as uuidv4 } from "uuid";
import * as faker from "faker";
export interface Item {
id: string;
name: string;
description?: string;
quantity: number;
createdAt: Date;
deletedAt?: Date;
}
export function generateFakeData(): Item {
return {
id: uuidv4(),
quantity: Math.random(),
name: faker.lorem.word(),
description: faker.lorem.words(),
createdAt: new Date(),
};
}
And once that is done let us run our Vue application.
$ npm run serve
Data Model
Since we are building a Grocery List application then we should model our data. The core model to have is an Item.
So to define the model,
export interface Item {
id: string;
name: string;
description?: string;
quantity: number;
createdAt: Date;
deletedAt?: Date;
}
So under the src
directory create a models
directory and it's where this Item model will reside. So create a file name it as item.model.ts
.
Then we'll have the following,
Pinia Setup
Open the main.ts
file under the src
directory and make sure to chain the following method use()
and pass in createPinia()
as the first parameter.
import { createPinia } from "pinia";
import { createApp } from "vue";
import App from "./App.vue";
createApp(App)
.use(createPinia())
.mount("#app");
Next is to create a directory and name it as store
and inside it create a file called index.ts
Then to define this main store,
import { generateFakeData, Item } from "@/models/item.model";
import { defineStore } from "pinia";
export type RootState = {
items: Item[];
};
export const useMainStore = defineStore({
id: "mainStore",
state: () =>
({
items: [],
} as RootState),
actions: {
createNewItem(item: Item) {
if (!item) return;
this.items.push(item);
},
updateItem(id: string, payload: Item) {
if (!id || !payload) return;
const index = this.findIndexById(id);
if (index !== -1) {
this.items[index] = generateFakeData();
}
},
deleteItem(id: string) {
const index = this.findIndexById(id);
if (index === -1) return;
this.items.splice(index, 1);
},
findIndexById(id: string) {
return this.items.findIndex((item) => item.id === id);
},
},
});
We have defined the most basic functionality, creating, updating an deleting an item from our grocery list. And that is more done enough for getting to know how to setup Pinia as your Vue store.
Demo
This is the best looking UI... Yeah.
As you can see from this setup we are able to use the Pinia store, that we are able to add an Item, update it and delete it.
Summary
We learned how to setup Pinia with Vue 3 and TypeScript. What I like Pinia is that it is built with TypeScript already that means the store provides us all the auto-completion that we want and the reason we love about TypeScript. Pinia is also very intuitive which we notice it was very similar to how Vuex is implemented.
But there are more of its features that you can read about from the official documentation
I hope you find this useful, cheers!
Full source code can be found from the repository
Top comments (5)
Hi, Carlo. Thought it was worth pointing out a latent bug in the example. Probably
as RootState
should never be used. You can see and experiment with the error at tsplay.dev/WkM3lNEssentially the clause
as RootState
is a type assertion, when what is probably intended is for that data structure to be validated as aRootState
by the compiler. Normally the keywordsas
andany
are a sign types are being bypassed (with the exception ofas const
).The type assertion is hiding an error in the stateFactory2 example below, but the compiler can't pick that up, since you have asserted the type and hence suspended the validation of the compiler.
The example stateFactory3 below is preferred, and DOES reveal the bug as a compiler error.
Hey Cefn, thank you so much for pointing this out! I haven't actually gotten into details regarding this so I highly appreciate your comment as it helps out for the other readers as well
I used Pinia for my project as well, just loved it.
It's so great and straightforward!
Is it required for the component to return RootState or can it be any type you want?