🏠 Home / Hub

🍍 Vue 15 — Pinia State Management

← Vue Menu · ← Prev: Router

Pinia = Vue 3 official state management library (Vuex ကို replace လုပ်တယ်)။ Composition API style, TypeScript friendly, Vue DevTools integration built-in ပါ။

1. Setup

npm install pinia

# main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)
app.use(createPinia())
app.mount('#app')

2. Define a Store (Setup Store — Recommended)

// stores/useProductStore.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useProductStore = defineStore('products', () => {
  // STATE
  const products = ref([])
  const loading  = ref(false)
  const error    = ref(null)
  const search   = ref('')

  // GETTERS (computed)
  const filtered = computed(() =>
    products.value.filter(p =>
      p.name.toLowerCase().includes(search.value.toLowerCase())
    )
  )
  const count = computed(() => products.value.length)
  const inStock = computed(() => products.value.filter(p => p.stock > 0))

  // ACTIONS
  async function fetchAll() {
    loading.value = true
    error.value   = null
    try {
      const res     = await fetch('/api/products')
      products.value = await res.json()
    } catch (e) {
      error.value = e.message
    } finally {
      loading.value = false
    }
  }

  function addProduct(product) {
    products.value.push(product)
  }

  function updateProduct(updated) {
    const idx = products.value.findIndex(p => p.id === updated.id)
    if (idx !== -1) products.value[idx] = updated
  }

  function removeProduct(id) {
    products.value = products.value.filter(p => p.id !== id)
  }

  // $reset() equivalent — restore initial state
  function reset() {
    products.value = []
    loading.value  = false
    error.value    = null
    search.value   = ''
  }

  return { products, loading, error, search, filtered, count, inStock,
           fetchAll, addProduct, updateProduct, removeProduct, reset }
})

3. Use Store in Components

<script setup>
import { storeToRefs } from 'pinia'
import { useProductStore } from '@/stores/useProductStore'
import { onMounted } from 'vue'

const store = useProductStore()

// storeToRefs: destructure reactive state (stays reactive)
// Actions don't need storeToRefs — destructure directly
const { products, loading, error, search, filtered, count } = storeToRefs(store)
const { fetchAll, addProduct, removeProduct }               = store

onMounted(() => fetchAll())
</script>

<template>
  <input v-model="search" placeholder="Search...">
  <p>{{ count }} products</p>
  <p v-if="loading">Loading...</p>
  <p v-if="error" style="color:red">{{ error }}</p>
  <div v-for="p in filtered" :key="p.id">
    {{ p.name }}
    <button @click="removeProduct(p.id)">Delete</button>
  </div>
</template>
storeToRefs() ကိုသုံးရင် destructured state ကိုပဲ reactive ဖြစ်နေနေမယ်။ actions တွေ (functions) ကို store မှ တိုက်ရိုက် destructure လုပ်ပါ — storeToRefs မလို။

4. Auth Store Example

// stores/useAuthStore.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useAuthStore = defineStore('auth', () => {
  const user  = ref(JSON.parse(localStorage.getItem('user') || 'null'))
  const token = ref(localStorage.getItem('token') || null)

  const isLoggedIn = computed(() => !!token.value)
  const isAdmin    = computed(() => user.value?.role === 'admin')

  async function login(email, password) {
    const res = await fetch('/api/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password }),
    })
    if (!res.ok) throw new Error('Invalid credentials')
    const data = await res.json()
    token.value = data.token
    user.value  = data.user
    localStorage.setItem('token', data.token)
    localStorage.setItem('user', JSON.stringify(data.user))
  }

  function logout() {
    token.value = null
    user.value  = null
    localStorage.removeItem('token')
    localStorage.removeItem('user')
  }

  return { user, token, isLoggedIn, isAdmin, login, logout }
})

5. Store Persistence Plugin

# Auto-save store state to localStorage/sessionStorage
npm install pinia-plugin-persistedstate

# main.js
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia)

# Store — add persist option
export const useCartStore = defineStore('cart', () => {
  const items = ref([])
  // ...
  return { items }
}, {
  persist: true,  // stores entire state in localStorage
  // or:
  persist: {
    key: 'my-cart',
    storage: sessionStorage,  // or localStorage
    paths: ['items'],         // only persist these fields
  }
})

6. Cart Store — Real World Example

// stores/useCartStore.js
export const useCartStore = defineStore('cart', () => {
  const items = ref([])  // [{product, qty}]

  const total   = computed(() => items.value.reduce((sum, i) => sum + i.product.price * i.qty, 0))
  const itemCount = computed(() => items.value.reduce((sum, i) => sum + i.qty, 0))

  function addItem(product, qty = 1) {
    const existing = items.value.find(i => i.product.id === product.id)
    if (existing) {
      existing.qty += qty
    } else {
      items.value.push({ product, qty })
    }
  }

  function removeItem(productId) {
    items.value = items.value.filter(i => i.product.id !== productId)
  }

  function updateQty(productId, qty) {
    const item = items.value.find(i => i.product.id === productId)
    if (item) item.qty = Math.max(1, qty)
  }

  function clear() { items.value = [] }

  return { items, total, itemCount, addItem, removeItem, updateQty, clear }
}, { persist: true })

7. Pinia vs Vuex vs Simple ref()

ScenarioSolution
Single component local stateref() / reactive() in component
Parent-child stateProps down, emits up
Sibling componentsPinia store (shared)
Global app state (user, cart, theme)Pinia store
Async data + loading/errorPinia action with try/catch
Persisted state (localStorage)Pinia + persistedstate plugin
Vuex (legacy)Migrate to Pinia (simpler, no mutations)

📌 Study Checklist