@danielkelly_io

@danielkellyio

Alabama, USA

Daniel Kelly

Teacher @ Vue School

Full Stack developer (10 years)

Husband and Father

#1 Training Platform for Vue.js

750+ Video Lessons

140,000 users

Alex Kyriakidis

Daniel Kelly

Debbie O'Brien

Maria Lamardo

Roman Kuba

Filip Rakowski

Lydia Hallie

Rolf Haug

Workshops

Vue Corporate Training

📺

Vue Video

Courses

👨‍🏫

Live Workshops

@hootlex

@hootlex

Amsterdam

Alex Kyriakidis

Founder of Vue School

Author of The Majesty of Vue.js

Vue.js Contributor

State Management in Vue.js with Pinia

Workshop Structure

Workshop Structure

👨‍🏫 Instruction

💬 Questions

👩‍💻 Hands-on Exercises

(10 - 20 mins)

(0 - 10 mins)

(15 - 30 mins)

📺 Solution

(5 - 10 mins)

Workshop Goal

Master Pinia

🎉 Have fun

  1. What is Pinia?
  2. Pinia State
  3. Pinia Actions
  4. Pinia Getters
  5. Using Stores in Other Stores
  6. Subscribe to Pinia Stores
  7. Pinia Plugins
  8. Advanced Concepts

Workshop Outline

Buddy System

🧑‍🤝‍🧑👭👬

✅ 🛑 👨‍🏫👩‍🏫

Meet Your Team

🧑‍🤝‍🧑👭👬

💬

💬

💬

What is Pinia?

1

What is Pinia?

Global state management solution

  • Created by Eduardo San Martin Morote

What is Pinia?

Global state management solution

  • Created by Eduardo San Martin Morote
  • An experiment to flesh out the next version of Vuex

What is Pinia?

Global state management solution

  • Created by Eduardo San Martin Morote
  • An experiment to flesh out the next version of Vuex
  • Became the recommended global state management solution in the latest official Vue.js docs

What is Pinia?

Global state management solution

What is Pinia?

Global state management solution

What is Pinia?

Global state management solution

https://vuejs.org/guide/scaling-up/state-management.html#pinia

What is Global State Management?

What is Global State Management?

Local State Management

<script setup>
import {ref} from "vue"
const count = ref(0)  
</script>
<script>
export default {
  data(){
    return {
      count: 0
    }
  }
}
</script>

Composition API

Options API

Props

Events

What is Global State Management?

What is Global State Management?

What is Global State Management?

What is Global State Management?

NOT a replacement for props, events, and local data

Local vs Global State

How do I know which is which?

 

  • Data needed throughout page
  • Data needed across multiple pages
  • Data is application specific

 

  • Specific to component instance
  • Component is reusable

Global State

Local Data

Local vs Global State

How do I know which is which?

Examples of local data

<!-- LoginForm.vue -->
<script setup>
import { ref } from "vue";  
const form = ref({
  username: "",
  password: ""
})
</script>
<template>
<form @submit="$emit('login', form)">
  <label>
    Username
    <input v-model="form.username">
  </label>

  <label>
    Password
    <input v-model="form.password">
  </label>
</form>
</template>
  • Form data

Local vs Global State

How do I know which is which?

Examples of local data

<!--TwitterFeed.vue-->

<script setup>
import { ref } from "vue";  
const loading = ref(false);
  
function loadTweets(){
  loading.value = true;
  // load the data...
  loading.value = false;
}
//...
</script>
<template>
  <!-- tweets...-->
  <AppSpinner v-if="loading"/>
</template>
  • Form data
  • Loading State

Local vs Global State

How do I know which is which?

Examples of global state

<!-- AppHeader.vue -->

<template>
  <!--...-->
	<a class="/me">
      {{ user.name }}
  	</a>
</template>
  • Authenticated user
<!-- MyPosts.vue -->
<script>
  const fetchPosts = ()=>{
    fetch(`https://myendpoint.com/user-posts/${user.id}`)
  }
//...
</script>
<!-- Profile.vue -->
<!-- ProfileEditor.vue -->
<!-- AppFooter.vue -->
<!-- etc -->

Local vs Global State

How do I know which is which?

Examples of global state

<!-- PostHero.vue -->
<template>
  <!--...-->
	<div class="hero">
      {{ post.title }}
  	</div>
</template>
  • Authenticated user
  • Blog post
<!-- PostShow.vue -->
<script>
  const fetchPost = ()=>{
    fetch(`https://myendpoint.com/posts/${post.id}`)
  }
//...
</script>
<!-- PostByLine.vue -->
<!-- PostComments.vue -->
<!-- PostBody.vue -->
<!-- etc -->

What about Vuex?

What about Vuex?

Pinia provides several advantages:

  • Pinia is modular by default (great for organization and code splitting)
  • Pinia removes mutations
  • Pinia is more intuitive
  • Pinia has full type support for TypeScript
  • We'll see examples of all of these throughout the workshop

What about Vuex?

https://vueschool.io/articles/vuejs-tutorials/how-to-migrate-from-vuex-to-pinia/

Can I use it with Vue 2?

Can I use it with Vue 2?

Yes!

  • (< 2.7) Must install the @vue/composition-api plugin
  • There are a few small caveats
  • Can use with composition API or Options API
  • During this workshop, we'll focus on Composition API

How to Install Pinia

How to Install Pinia

npm init vue@3

for a new project

How to Install Pinia

npm install pinia
// main.js
import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia";


createApp(App)
  .use(createPinia())
  .mount("#app");

in an existing project

Create Your First Store

Create Your First Store

Pinia is modular by default

Create Your First Store

Pinia is modular by default

Create Your First Store

Pinia is modular by default

Store

Posts

Store

Users

Users

Store

Comments

Blog Application

Create Your First Store

// stores/PostStore.js

import { defineStore } from 'pinia' 
  • a store is a regular .js or .ts file
  • lives in a "stores" directory by convention
  • use defineStore from Pinia define file as a store

Create Your First Store

// stores/PostStore.js

import { defineStore } from 'pinia' 

defineStore('PostStore')
  • first argument is store name
  • name is required
  • this name will show in the devtools
  • My preference to name it the same as the filename

Create Your First Store

// stores/PostStore.js

import { defineStore } from 'pinia' 

defineStore('PostStore', {
  // state
  // actions
  // getters
})

second argument is store options object

Create Your First Store

// stores/PostStore.js

import { defineStore } from 'pinia' 

defineStore('PostStore', () => {
  return { 
    // data
    // functions
    // etc
  }
})

second argument can also be a function similar to component setup()

Create Your First Store

// stores/PostStore.js

import { defineStore } from 'pinia' 

export const usePostStore = defineStore('PostStore', {
  // state
  // actions
  // getters
})
  • expose the store by exporting it
  • convention to prefix with `use`

Create Your First Store

// stores/PostStore.js

import { defineStore } from 'pinia' 

export const usePostStore = defineStore('PostStore', {
  // state
  // actions
  // getters
})



if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(usePostStore, import.meta.hot));
}

support HMR but must provide a snippet

Create Your First Store

// App.vue
<script setup>
import { usePostStore } from "@/stores/PostStore"
const postStore = usePostStore();
</script>

use the store by importing the use function and calling it

Create Your First Store

Questions?

What We're Buliding

What We're Buliding

What We're Buliding

Let's take a look at:

  • The boilerplate code
  • and the existing app in the browser

 

Assignment #1 (15 mins)

👩‍💻👨🏽‍💻

Pinia State

2

Define Pinia State

// stores/ProductStore.ts

import { defineStore } from "pinia";

export const useProductStore = defineStore("ProductStore", {
  state: () => {
    return {};
  },
});

Must be a function

  • Allows Pinia to work on both client side and server side
  • the arrow function is recommended for full type inference

Define Pinia State

// stores/ProductStore.ts

import { defineStore } from "pinia";

export const useProductStore = defineStore("ProductStore", {
  state: () => {
    return {
       	name: "The Pineapple Stand",
      	products: [
          "Plain Ol' Pineapple",
          "Dried Pineapple",
          "Pineapple Gum",
          "Pineapple T-Shirt",
      	],
      	isAwesome: true,
    };
  },
});

Access Pinia State

// stores/App.vue

<script setup>
import { useProductStore } from "./stores/ProductStore";
const productStore = useProductStore();
console.log(productStore.name) // "The Pineapple Stand"
</script>

...

State available as property on store

Access Pinia State

State is TypeSafe

Access Pinia State

// stores/App.vue

<script setup>
import { useProductStore } from "./stores/ProductStore";
const { name } = useProductStore();
console.log(name) // "The Pineapple Stand"
</script>

...

⚠️ Name is no longer reactive

Can de-structure state from store

Access Pinia State

// stores/App.vue

<script setup>
import { useProductStore } from "./stores/ProductStore";
import { storeToRefs } from "pinia";
  
const { name } = storeToRefs(useProductStore());
console.log(name.value) // "The Pineapple Stand"
</script>

...

When de-structuring convert store to refs to maintain reactivity

Update Pinia State

// stores/App.vue

<script setup>
import { useProductStore } from "./stores/ProductStore";
import { storeToRefs } from "pinia";
const { name } = storeToRefs(useProductStore());
name.value = "Hello new name";
</script>

State can be directly updated

Update Pinia State

// stores/App.vue

<script setup>
import { useProductStore } from "./stores/ProductStore";
import { storeToRefs } from "pinia";
const productStore = useProductStore();
productStore.name = "Hello new name";
</script>

No need for .value if you don't de-destructure

Update Pinia State

// stores/App.vue

<script setup>
import { useProductStore } from "./stores/ProductStore";
import { storeToRefs } from "pinia";
const { name } = storeToRefs(useProductStore());
</script>

<template>
<input v-model="name" type="text" />
</template>

Use with v-model is easy!

Update Pinia State

Update Pinia State

// stores/App.vue

<script setup>
import { useProductStore } from "./stores/ProductStore";
import { storeToRefs } from "pinia";
const { name: newName } = storeToRefs(useProductStore());
</script>

Can rename de-structured state

Pinia State

Useful feature you don't get if you roll your own stores with vanilla composition API.

Pinia State

Comes with some built in helpers

store.$reset()
// acccess the entire state or 
// set it all at once
store.$state

Questions?

 

Assignment #2 (20 mins)

👩‍💻👨🏽‍💻

15 Minute Break

☕️

Pinia Actions

3

Pinia Actions

What are they?

  • Methods for mutating the store's state
  • can think of them like component methods but for the store
  • provide standardized and named mechanisms for mutating the state
  • are great for feedback in the devtools
  • consolidate business logic
  • can be asynchronous or synchronous

Pinia Actions

import { defineStore } from "pinia";
import products from "@/data/products.json";
export const useProductStore = defineStore("ProductStore", {
  state: //...
  actions:{
    fill(){}
  }
});

Define actions as methods on the actions option

Pinia Actions

import { defineStore } from "pinia";
export const useProductStore = defineStore("ProductStore", {
  state: ()=>{
    return {
      products:[],
    }
  },
  actions:{
    async fill(){
      const res = await fetch('my_api_endpoint/products');
      this.products = await res.json();
    }
  }
});

access state with `this`

Great for filling state with initial data

Pinia Actions

import { defineStore } from "pinia";
export const useProductStore = defineStore("ProductStore", {
  state: ()=>{
    return {
      products:[],
    }
  },
  actions:{
    async someOtherAction(){},
    async fill(){
      const res = await fetch('my_api_endpoint/products');
      this.products = await res.json();
      this.someOtherAction()
    }
  }
});

access other actions with `this`

Great for filling state with initial data

Pinia Actions

State is fully typed on `this` within actions

Pinia Actions

Call actions in components as store methods

<script setup>
const productStore = useProductStore();
productStore.fill();
</script>

Pinia Actions

Or de-structure it from the store

<script setup>
const { fill } = useProductStore();
fill();
</script>

Pinia Actions

You CANNOT get actions when using storeToRefs

<script setup>
const { fill } = storeToRefs(useProductStore());
fill(); // Error
</script>

won't work

Pinia Actions

import { defineStore } from "pinia";

export const useCartStore = defineStore("CartStore", {
  state: () => {
    return {
      items: [],
    };
  },
  actions: {
    addItem(itemId, count) {
      // set the count for the proper item in the state above
    },
  },
});

Useful for updating state based on user interaction

// App.vue
<button @click="addItem(product.id, $event)">Add Product to Cart</button>

Pinia Actions

Mutations grouped in the devtools

Pinia $patch

Alternate way of grouping mutations to state

productStore.$patch({
  name: "Vue Conf Toronto",
  location: "Toronto, Canada",
});

Pinia $patch

Alternate way of grouping mutations to state

Pinia $patch

Can also use a callback function

cartStore.$patch((state) => {
  state.items.push({ name: 'shoes', quantity: 1 })
  state.hasChanged = true
})

Questions?

 

Assignment #3 (20 mins)

👩‍💻👨🏽‍💻

Pinia Getters

4

Pinia Getters

What are they?

equivalent of computed props on a component

Pinia Getters

// ProductStore.ts
import { defineStore } from "pinia";
import type { Product } from "@/types";
export const useProductStore = defineStore("ProductStore", {
  state: () => {
    return {
      products: [] as Product[],
    };
  },
  getters: {
    count() {
      return this.products.length;
    },
  },
  actions: {
    //...
  },
});

access state with `this`

Pinia Getters

// ProductStore.ts
import { defineStore } from "pinia";
import type { Product } from "@/types";
export const useProductStore = defineStore("ProductStore", {
  state: () => {
    return {
      products: [] as Product[],
    };
  },
  getters: {
    count(): number {
      return this.products.length;
    },
  },
  actions: {
    //...
  },
});

access state with `this`

must explicitly type the return

Pinia Getters

// ProductStore.ts
import { defineStore } from "pinia";
import type { Product } from "@/types";
export const useProductStore = defineStore("ProductStore", {
  state: () => {
    return {
      products: [] as Product[],
    };
  },
  getters: {
    count: (state) => {
      return state.products.length;
    },
  },
  actions: {
    //...
  },
});

access state with `state`

Pinia Getters

// ProductStore.ts
import { defineStore } from "pinia";
import type { Product } from "@/types";
export const useProductStore = defineStore("ProductStore", {
  state: () => {
    return {
      products: [] as Product[],
    };
  },
  getters: {
    count: (state) => state.products.length,
  },
  actions: {
    //...
  },
});

encourages single

line arrow functions

No need to explicitly type return

Pinia Getters

// ProductStore.ts
import { defineStore } from "pinia";
import type { Product } from "@/types";
export const useProductStore = defineStore("ProductStore", {
  state: () => {
    return {
      products: [] as Product[],
    };
  },
  getters: {
    count: (state) => state.products.length,
    doubleCount(): number {
      return this.count * 2;
    },
  },
  actions: {
    //...
  },
});

access other

getters on `this`

Pinia Getters

// ProductStore.ts
import { defineStore } from "pinia";
import type { Product } from "@/types";
export const useProductStore = defineStore("ProductStore", {
  state: () => {
    return {
      products: [] as Product[],
    };
  },
  getters: {
    count: (state) => state.products.length,
    doubleCount: (state) => state.count * 2,
    productByName: (state) => (name) => {
      return state.products.find((product) => product.name === name);
    },
  },
});

accept arguments

by returning a function

Dynamic Getters

// ProductStore.ts
import { defineStore } from "pinia";
import type { Product } from "@/types";
export const useProductStore = defineStore("ProductStore", {
  state: () => {
    return {
      products: [] as Product[],
    };
  },
  getters: {
    count: (state) => state.products.length,
    doubleCount: (state) => state.count * 2,
    productByName(state) {
      return function (name) {
        return state.products.find((product) => product.name === name);
      };
    },
  },
});

accept arguments

by returning a function

Pinia Getters

Dynamic Getters

Pinia Getters

// App.vue
<script setup>
const productStore = useProductStore();
console.log(productStore.count) // 4
</script>

Access getters as a property on the store

Pinia Getters

// App.vue
<script setup>
import { storeToRefs } from "pinia";
const { count } = storeToRefs(useProductStore());
console.log(count.value) // 4
</script>

Can de-structure getters from store but must use `storeToRefs`

Questions?

 

Assignment #4 (20 mins)

👩‍💻👨🏽‍💻

Using Stores in Other Stores

5

Using Stores in Other Stores

// CartStore.ts
import { defineStore } from "pinia";
import { useProductStore } from "./ProductStore";

export const useCartStore = defineStore("CartStore", {
  // ...
  getters: {
    allProducts(){
      const productStore = useProductStore();
      return productStore.products
    }
  },
  //...
});

Use state and getters from another store in a getter or an action

Using Stores in Other Stores

// CartStore.ts
import { defineStore } from "pinia";
import { useProductStore } from "./ProductStore";

export const useCartStore = defineStore("CartStore", {
  //...
  actions:{
    reloadProducts(){
      const productStore = useProductStore();
      return productStore.fill()
    }
  }
  //...
});

Use actions from another store in an action

Using Stores in Other Stores

// CartStore.ts
import { defineStore } from "pinia";
import { useProductStore } from "./ProductStore";

❌ const productStore = useProductStore();

export const useCartStore = defineStore("CartStore", {
  //...
  actions:{
    reloadProducts(){
      return productStore.fill()
    }
  }
	//...
});

Use actions from another store in an action

Using Stores in Other Stores

// CartStore.ts
import { defineStore } from "pinia";
import { useProductStore } from "./ProductStore";

❌ const productStore = useProductStore();

export const useCartStore = defineStore("CartStore", {
  //...
  actions:{
    reloadProducts(){
      return productStore.fill()
    }
  }
	//...
});

Use actions from another store in an action

Questions?

 

Assignment #5 (15 mins)

👩‍💻👨🏽‍💻

Subscribe to Pinia Stores

6

Subscribe to Pinia Stores

  • watch state for changes
  • watch actions for calls
  • and perform side affects

Subscribe to Pinia Stores

  • watch state for changes
  • watch actions for calls
  • and perform side affects
  • measuring how long your actions take to run
  • triggering user notifications
  • logging errors to 3rd party services

Subscribe to Pinia Stores

someStore.$onAction(
  ({
    name, // name of the action
    store, // store instance, same as `someStore`
    args, // array of parameters passed to the action
    after, // hook after the action returns or resolves
    onError, // hook if the action throws or rejects
  }) => {
    console.log(name, store, args, after, onError);
  }
);

Subscribe to actions

Subscribe to Pinia Stores

someStore.$onAction(
  ({
    name, // name of the action
    store, // store instance, same as `someStore`
    args, // array of parameters passed to the action
    after, // hook after the action returns or resolves
    onError, // hook if the action throws or rejects
  }) => {
    if(name === 'fill'){
      //... do the things here  
    }
  }
);

Use conditional to run on select actions

Subscribe to Pinia Stores

someStore.$onAction(
  ({
    name, // name of the action
    store, // store instance, same as `someStore`
    args, // array of parameters passed to the action
    after, // hook after the action returns or resolves
    onError, // hook if the action throws or rejects
  }) => {
    after( result =>{
      console.log(`the action is complete and returned ${result}`)
    })
    onError(error =>{
      console.log(`the action failed and the error thrown is ${error}`)
    })
  }
);

Example of after and onError

Subscribe to Pinia Stores

someStore.$onAction(
  ({
    name, // name of the action
    store, // store instance, same as `someStore`
    args, // array of parameters passed to the action
    after, // hook after the action returns or resolves
    onError, // hook if the action throws or rejects
  }) => {
    after( result =>{
      console.log(`the action is complete and returned ${result}`)
    })
    onError(error =>{
      console.log(`the action failed and the error thrown is ${error}`)
    })
  }
);

Example of after and onError

Subscribe to Pinia Stores

someStore.$subscribe((mutation, state) => {
  
  // 'direct' | 'patch object' | 'patch function'
  mutation.type 
  
  // same as cartStore.$id
  mutation.storeId
  
  // only available with mutation.type === 'patch object'
  mutation.payload // patch object passed to $patch()
})

Subscribing to the state

Subscribe to Pinia Stores

someStore.$subscribe((mutation, state) => {
  
  // 'direct' | 'patch object' | 'patch function'
  mutation.type 
  
  someStore.myState = "something else"
})

Subscribing to the state

Subscribe to Pinia Stores

someStore.$subscribe((mutation, state) => {
  
  // 'direct' | 'patch object' | 'patch function'
  mutation.type 
  
  someStore.$patch({
    myState: "something else",
    otherState: true
  })
})

Subscribing to the state

Subscribe to Pinia Stores

someStore.$subscribe((mutation, state) => {
  
  // 'direct' | 'patch object' | 'patch function'
  mutation.type 
  
  someStore.$patch((state)=>{
    state.myState = "something else"
  })
})

Subscribing to the state

Subscribe to Pinia Stores

someStore.$subscribe((mutation, state) => {
  
  // 'direct' | 'patch object' | 'patch function'
  mutation.type 
  
  // same as cartStore.$id
  mutation.storeId
  
  // only available with mutation.type === 'patch object'
  mutation.payload // patch object passed to $patch()
})

Subscribing to the state

Subscribe to Pinia Stores

someStore.$subscribe((mutation, state) => {
  
  // 'direct' | 'patch object' | 'patch function'
  mutation.type 
  
  // same as cartStore.$id
  mutation.storeId

  defineStore("someStore", {/*...*/})
})

Subscribing to the state

Subscribe to Pinia Stores

someStore.$subscribe((mutation, state) => {
  
  // 'direct' | 'patch object' | 'patch function'
  mutation.type 
  
  // same as cartStore.$id
  mutation.storeId
  
  // only available with mutation.type === 'patch object'
  mutation.payload // patch object passed to $patch()
})

Subscribing to the state

Subscribe to Pinia Stores

someStore.$subscribe((mutation, state) => {
  localStorage.setItem('myState', JSON.stringify(state))
})

Subscribing to the state

Questions?

 

Assignment #6 (25 mins)

👩‍💻👨🏽‍💻

Pinia Plugins

7

Pinia Plugins

Just like Vue has plugins to quickly add advanced functionality...

Pinia Plugins

...so does Pinia

Pinia Plugins

One advantages of the standardization provided by an official store solution over vanilla CAPI store

Pinia Plugins

What can they do?

  • Add new properties to stores
  • Add new options when defining stores
  • Add new methods to stores
  • Wrap existing methods
  • Change or even cancel actions
  • Implement side effects like Local Storage

Pinia Plugins

How to build

//PiniaLocalStoragePlugin.ts
export function PiniaLocalStoragePlugin({ 
  pinia, 
  app, 
  store, 
  options
}) {
  
}
// the pinia created with `createPinia()`
// the current app created with `createApp()` (Vue 3 only)
// the store the plugin is augmenting
// the options object defining the store passed to `defineStore()`

Pinia Plugins

How to build

//PiniaLocalStoragePlugin.ts
export function PiniaLocalStoragePlugin({ 
  pinia, 
  app, 
  store, 
  options
}) {
  const localStorageKey = `PiniaStore_${store.$id}`;
  
  store.$subscribe((mutation, state) => {
    localStorage.setItem(localStorageKey, JSON.stringify(state));
  });
  
  const savedState = localStorage.getItem(localStorageKey);
  if (savedState) {
    store.$state = JSON.parse(savedState);
  }
  
}

Pinia Plugins

Applies to all stores by default

//PiniaLocalStoragePlugin.ts
export function PiniaLocalStoragePlugin({ 
  pinia, 
  app, 
  store, 
  options
}) {
  const localStorageKey = `PiniaStore_${store.$id}`;
  
  store.$subscribe((mutation, state) => {
    localStorage.setItem(localStorageKey, JSON.stringify(state));
  });
  
  const savedState = localStorage.getItem(localStorageKey);
  if (savedState) {
    store.$state = JSON.parse(savedState);
  }
  
}

Pinia Plugins

Making it opt-in

//CartStore.ts
export const useCartStore = defineStore("CartStore", {
  localStorage: true,
  state: ()=>{ /*...*/ },
  getters: {/*...*/}
})

Pinia Plugins

Making it opt-in

//PiniaLocalStoragePlugin.ts
export function PiniaLocalStoragePlugin({ 
  pinia, 
  app, 
  store, 
  options
}) {
  if (!options.localStorage) return;
  
  const localStorageKey = `PiniaStore_${store.$id}`;
  
  store.$subscribe((mutation, state) => {
    localStorage.setItem(localStorageKey, JSON.stringify(state));
  });
  
  const savedState = localStorage.getItem(localStorageKey);
  if (savedState) {
    store.$state = JSON.parse(savedState);
  }
  
}

Pinia Plugins

Type the new option

import "pinia";

declare module "pinia" {
  export interface DefineStoreOptionsBase<S, Store> {
    // allow defining a boolean
    // for local storeage plugin
    localStorage?: boolean;
  }
}

Pinia Plugins

extend the DefineStoreOptionsBase

// main.ts
//...
import { PiniaLocalStoragePlugin } from "./plugins/...";

const pinia = createPinia()
	.use(PiniaLocalStoragePlugin);

createApp(App)
  .use(pinia)
  .mount("#app");

Pinia Plugins

Use the plugin

//PiniaAxiosPlugin.ts
import axios from "axios";

export function PiniaAxiosPlugin({ 
  pinia, 
  app, 
  store, 
  options
}) {
  store.axios = axios;
  
}

Pinia Plugins

Another example: adding axios

// ProductStore.ts

export const useProductStore = defineStore("ProductStore", {
  //...
  actions: {
    async fill() {
      const res = await this.axios.get("/products.json");
      this.products = res.data;
    },
  },
});

Pinia Plugins

Another example: adding axios

//PiniaAxiosPlugin.ts
import axios from "axios";
import { markRaw } from "vue";

export function PiniaAxiosPlugin({ 
  pinia, 
  app, 
  store, 
  options
}) {
  store.axios = markRaw(axios);
}

Pinia Plugins

Another example: adding axios

Pinia Plugins

Typing axios on stores

import "pinia";
import type { Axios } from "axios";

declare module "pinia" {
  export interface PiniaCustomProperties {
    axios: Axios;
  }
}

Pinia Plugins

Typing axios on stores

Pinia Plugins

3rd party pinia plugins

  • There is no awesome Pinia repo
  • Can search npm for Pinia plugins

Questions?

 

Assignment #7 (25 mins)

👩‍💻👨🏽‍💻

Defining a Store Using a Setup Function

8

Defining a Store Using a Setup Function

//CounterStore.ts
export const useCounterStore = defineStore('CounterStore', () => {
  const count = ref(0)
  function increment() {
    count.value++
  }

  return { count, increment }
})

Defining a Store Using a Setup Function

Why?

Defining a Store Using a Setup Function

Why?

  • style preference
  • can watch state from within the store
  • consistent syntax with components
  • Can have private actions or getters

Defining a Store Using a Setup Function

Limitations

  • No this, no $patch(), $reset(), etc
  • Must return ALL state properties owned by the store

Defining a Store Using a Setup Function

//CounterStore.ts
export const useCounterStore = defineStore('CounterStore', () => {
  const count = ref(0)
  function increment() {
    count.value++
  }
  
  watch(count, ()=>{
    // save some data to the server or whatever
  })

  return { count, increment }
})

Defining a Store Using a Setup Function

//CounterStore.ts
import { watchDebounced } from '@vueuse/core'

export const useCounterStore = defineStore('CounterStore', () => {
  const count = ref(0)
  function increment() {
    count.value++
  }
  
  watchDebounced(count, ()=>{
    // save some data to the server or whatever
  }, { debounce: 500 })

  return { count, increment }
})

Questions?

 

Assignment #8 (25 mins)

👩‍💻👨🏽‍💻

Q&A

Pinia: The Enjoyable Vue Store

Other Great Courses Too!

Black Friday Special

Workshop

Vue.js Fundamentals

TypeScript + Vue Workshop

Vue 3 Composition API Workshop

Nuxt 3 Fundamentals Workshop

Vue.js Certification

Thanks!

1. Pinia Workshop

By Daniel Kelly

1. Pinia Workshop

  • 1,792