#vue
#javascript
#frontend
Day 12: State Management with Pinia
Welcome to Day 12! When your app grows, passing props down 10 levels (“prop drilling”) becomes a nightmare. We need a Global Store.
Enter Pinia, the official state management library for Vue. It feels just like the Composition API.
Setup
npm install pinia
main.ts
import { createPinia } from 'pinia'
app.use(createPinia())
Defining a Store
A store is a bucket of logic. Variables are state, Computed are getters, and Functions are actions.
stores/counter.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
// Name 'counter' must be unique
export const useCounterStore = defineStore('counter', () => {
// State
const count = ref(0)
// Getters
const doubleCount = computed(() => count.value * 2)
// Actions
function increment() {
count.value++
}
return { count, doubleCount, increment }
})
Using the Store
Using it is incredibly simple. Just import it and call it.
App.vue
<script setup lang="ts">
import { useCounterStore } from './stores/counter'
import { storeToRefs } from 'pinia'
const store = useCounterStore()
// Destructuring state directly breaks reactivity!
// Use storeToRefs for state/getters
const { count, doubleCount } = storeToRefs(store)
// Actions can be destructured directly
const { increment } = store
</script>
<template>
<h1>Count: {{ count }}</h1>
<h2>Double: {{ doubleCount }}</h2>
<button @click="increment">Add</button>
</template>
Note: This
countis shared across the entire app. If Component B usesuseCounterStore(), it sees the exact same count.
Challenge for Day 12
- Create a
useUserStore. - State:
username(string),isLoggedIn(boolean). - Actions:
login(name),logout(). - In
App.vue, show “Welcome, {{ username }}” if logged in, otherwise show a “Login” button.
Solution:
stores/user.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useUserStore = defineStore('user', () => {
const username = ref('')
const isLoggedIn = ref(false)
function login(name: string) {
username.value = name
isLoggedIn.value = true
}
function logout() {
username.value = ''
isLoggedIn.value = false
}
return { username, isLoggedIn, login, logout }
})
App.vue
<script setup lang="ts">
import { useUserStore } from './stores/user'
import { storeToRefs } from 'pinia'
const store = useUserStore()
const { username, isLoggedIn } = storeToRefs(store)
</script>
<template>
<div v-if="isLoggedIn">
<p>Welcome, {{ username }}</p>
<button @click="store.logout()">Logout</button>
</div>
<div v-else>
<button @click="store.login('Jack')">Login as Jack</button>
</div>
</template>
You have successfully lifted state up! Tomorrow, we look at how to handle Async operations (API calls) within this architecture.