Pinia – The Vue Store Library
What is Pinia
Pinia is a library store for Vue.js. It will work with Vue.js 3 as well as Vue.js 2. Pinia holds the state and business logic which is not bounded with component object trees. In other words, I would say it creates a global state where everybody can read and write the data. Pinia has three area’s as follows,
- State
- Getters
- Action
We can assume and compare these things with data, computed and methods in components.
Why Pinia (A Store)
Store will have the data that can be used everywhere in our application. Meanwhile we should avoid the local data in the store which can be added in the component instead. Pinia is a choice if your application needs to access the global state values.
Features of Pinia
- Hot module replacement
- Modify your stores without reloading your page
- Keep any existing state while developing
- Plugins: extend Pinia features with plugins
- Proper TypeScript support or autocompletion for JS users
- Server Side Rendering Support
- Devtools support
- A timeline to track actions, mutations
- Stores appear in components where they are used
- Time travel and easier debugging
Full Tutorial
Installations
You can install pinia with the help of NPM or Yarn as follows,
npm install pinia
or
yarn add pinia
Inject Pinia with CreateApp
Here we are going to have only one store for the application. So we have to create its instance and inject the same in VUE createApp. You can call it a root store.
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
So what createPinia does here ?. It creates the entity which holds the state for our application. And It’s global state where one can read and write as well. It has three concepts or property’s
- state
- getters
- actions
(You can assume this three components with data, computed and methods)
Create the Store
First we need to create the store template. We can create a store template using defineStore() and it requires a unique name. We may use this template across our application.
import { defineStore } from 'pinia'
// useStore could be anything like useUser, useCart
// the first argument is a unique id of the store across your application
export const useStore = defineStore('main', {
// other options...
})
Second we need to use this store template inside the setup() function so that it will create the store. We have to call useStore() as follows,
import { useStore } from '@/stores/counter'
export default {
setup() {
const store = useStore()
return {
// you can return the whole store instance to use it in the template
store,
}
},
}
Once the store is created you can access its properties like state, getters and actions.
State
The state is the heart of the system. It is a function that returns the initial state of the application.
import { defineStore } from 'pinia'
const useStore = defineStore('storeId', {
// arrow function recommended for full type inference
state: () => {
return {
// all these properties will have their type inferred automatically
counter: 0,
name: 'Eduardo',
isAdmin: true,
}
},
})
We can access the store to read and write its data through store instance, and we can reset it to the initial state as well
const store = useStore()
store.counter++
store.$reset()
Getter
We can define the getters property in defineStore() template. I would say, it is a replacement for computed values.
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
getters: {
// automatically infers the return type as a number
doubleCount(state) {
return state.counter * 2
},
// the return type **must** be explicitly set
doublePlusOne(): number {
// autocompletion and typings for the whole store
return this.doubleCount + 1
},
},
})
Then we can access and use the getters inside the template as follows,
<template>
<p>Double count is {{ store.doubleCount }}</p>
</template>
<script>
export default {
setup() {
const store = useStore()
return { store }
},
}
</script>
Actions
Actions are the replacement for the methods. We can add our actions in the defineStore() template. Unlike getters, we can write asynchronous operations inside action. It will useful for creating API calls for our business logic
import { mande } from 'mande'
const api = mande('/api/users')
export const useUsers = defineStore('users', {
state: () => ({
userData: null,
// ...
}),
actions: {
async registerUser(login, password) {
try {
this.userData = await api.post({ login, password })
showTooltip(`Welcome back ${this.userData.name}!`)
} catch (error) {
showTooltip(error)
// let the form component display the error
return error
}
},
},
})
Summary
- Pinia is a store library for Vue, it allows you to share a state across components/pages.
- Compared to Vuex, Pinia provides a simpler API with less ceremony, offers Composition-API-style APIs, and most importantly, has solid type inference support when used with TypeScript.
- The formula is Create the template -> create the store with state, getters and actions and use across the application.