Pinia is one of the newest projects from the Vue ecosystem and it is the new official state management tool for Vue.js apps. Its API is similar to Vuex (its predecessor) and it is designed to be faster and more lightweight. According to Pinia's Official Website, Pinia was born out of the experiment to redesign what a Store for Vue could look like with the Composition API. It is an upgrade on Vuex and compatible with Vue 2 and options API.
Note: This tutorial uses <script setup>
, composition API and Vue 3.
Installation
Run the following code in your terminal to create a new Vue project.
Input the project name in the command prompt.
Select Vue.
Select yes when the command prompt asks to add Pinia to state management system.
npm create vite@latest
Pinia can be installed in an existing application with npm. Run the following command in your terminal to install Pinia in an existing application.
npm install pinia
//or
yarn add pinia
Project Configuration
The application has to know to use Pinia, so we tell it to use it in the main.js
file.
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import './style.css'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
We imported createPinia
from Pinia
, instantiated it and instructed the application to use it by simply adding it to the Vue application's instance.
Pinia Store Setup
Create a stores
folder in the src
folder. In the newly created stores
folder, create a products.js
file.
The store file is projects.js
in our case here
Next, we need to import defineStore
in the products.js
file and use it.
import { defineStore } from "pinia"
const useProductsStore = defineStore("products", {
state: () => ({
products: [
description:
"Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday",
category: "men's clothing",
image: "https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg",
rating: { rate: 3.9, count: 120 },]
}),
getters: {},
actions: {},
})
We use the defineStore()
method by:
Assigning it to a variable. Even though you can name the variable anything, the convention is to start the name use, then the name of the store file in singular and end it with store. In this case, the variable name is
useProducutStore
The
defineStore()
takes an optional name which is the name of the store and an options object. This serves as an id for the store.The options object houses the
state
,setters
andactions
objects and I have added aproducts
array that contains a single product.
Unlike in Vuex, Pinia does not have a mutations
object, the actions
are where mutation of state and asynchronous request happens.
The options object is very similar to the options API. The setters are like the computed properties and actions, like methods.
I love this because not being able to put state mutation and methods that make API calls in one object has been a pain. It has led to having methods with similar names both in mutations and actions, the one in actions makes the API call while the one in mutations adds the fetched data into the state.
We are going to add a couple of states and methods to the options object.
import { defineStore } from "pinia"
export const useProductsStore = defineStore("products", {
state: () => ({
counter: 0,
categories: [],
products: [
{
id: 1,
title: "Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops",
price: 109.95,
description:
"Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday",
category: "men's clothing",
image: "https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg",
rating: { rate: 3.9, count: 120 },
},
],
}),
getters: {
getCounter: state => state.counter,
getAllProducts: state => state.products.length,
},
actions: {
incrementCounter() {
this.counter++
},
addProduct (product) {
if (!product.length) return
this.products.push(product)
},
async getCategories() {
await fetch("https://fakestoreapi.com/products/categories")
.then(res => res.json())
.then(data => {
this.categories.push(data)
})
},
},
})
In the code above, I have added:
categories
state to store the categories.counter
state to count arbitrary numbers.getCounter()
getter to get the currentcounter
's count.getAllProducts()
getter all products.incrementCounter()
action to increment the counter.addProduct()
action to add a product.getCategories()
action to make an asynchronous call to an endpoint that gets all categories and populate them to state.
In the actions
object, the state is not passed into the functions, rather, they are accessed through this
keyword. this
return the current store instance. This implies that all actions
functions must be regular functions and not arrow functions.
import { defineStore } from "pinia"
export const useProductsStore = defineStore("products", {
...
getters: {
getCounter() {
this.counter
},
getAllProducts() {
this.products.length
},
},
...
})
In the getters objects, the state can be substituted with this
keyword, which returns the store instance. Meaning, the arrow functions have to be changed to regular functions.
How to access Pinia getters in another getter
Getters can be accessed and used in another getter using this
keyword.
getters: {
...
getAllProducts() {
return this.products.length
},
doubleProductsSize() {
return this.getAllProducts * 2
},
},
We doubled the size of all the products that were returned by getAllProducts()
getter in the new doubleProductsSize()
getter.
How to access Pinia actions in another getter
Actions can be accessed and used in another action using this
keyword.
actions: {
incrementCounter() {
this.counter = this.counter + 1
},
...
async getCategories() {
this.incrementCounter()
await fetch("https://fakestoreapi.com/products/categories")
.then(res => res.json())
.then(data => {
this.categories.push(data)
})
},
},
We incremented the counter each time we get all products categories by calling incrementCounter()
action in the getCategories()
async action.
How to access Pinia state in Vue components
In the components, Pinia states are accessed by importing the store in the component and passing it into a storeToRefs()
method provided by Pinia where they can be destructured before they are used. storeToRefs()
helps to extract the state while keeping its reactivity.
import { useProductsStore } from "./stores/products"
import { storeToRefs } from "pinia"
const store = useProductsStore()
const { categories, counter, products } = storeToRefs(store)
How to access Pinia getters in Vue components
Getters can be accessed on the object instance.
import { useProductsStore } from "./stores/products"
const store = useProductsStore()
{{ store.getCounter }}
{{ store.getAllProducts }}
They can also be destructured by passing the store object instance through the storeToRefs()
method.
import { useProductsStore } from "./stores/products"
const store = useProductsStore()
const { getCounter, getAllProducts } = storeToRefs(store)
How to access Pinia actions in Vue components
Action methods can be destructured off the store object instance.
import { useProductsStore } from "./stores/products"
const store = useProductsStore()
const { getCategories } = store
onBeforeMount(() => getCategories())
They can be accessed on the store object instance directly.
import { useProductsStore } from "./stores/products"
const store = useProductsStore()
onBeforeMount(() => store.getCategories())
Conclusion
Pinia is a state management library for Vue.js that provides a way to manage and share reactive states across components in a Vue.js application.
In this tutorial, we learned how to create a Pinia store using the defineStore()
function and use it in a Vue.js component. We saw how to define state properties, getters and actions functions that can mutate the state and perform asynchronous calls in the store, and how to access them in a component using the useStore()
and storeToRefs()
functions provided by Pinia.
Pinia provides a powerful and flexible way to manage states in Vue.js applications, and it's well-suited to large and complex applications where state management can become a challenge. Its use of the Composition API makes it easy to use and understand, and its integration with Vue.js 3 means that it works seamlessly with other Vue.js features and libraries.
Overall, Pinia is a great choice for developers looking for a modern and flexible state management library for their Vue.js applications. It provides a solid foundation for building complex and scalable applications, and its simple and intuitive API makes it easy to learn and use.
Top comments (0)