#
Creating a store
Stores are created in a stores/
directory, and defined by using Pinia's defineStore
method.
In this example, we have created a store (useCounterStore
) and given the store a name (counter
). We have then defined our state
property (count
) with an initial value.
// stores/counter.ts
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
})
#
Using the store
Pinia offers a few ways to access the store and maintain reactivity.
#
created: 2023-05-25T02:57:41 (UTC +02:00)
tags: []
source: https://dev.to/tao/adding-pinia-to-nuxt-3-2023-3l77
author:
#
Adding Pinia to Nuxt 3 🍍 (2023) - DEV Community
# ExcerptGetting started with Pinia for global state management.
#
1. Store instance
In your component's setup()
, import the store's useStore()
method.
// components/MyCounter.vue
import { useCounterStore } from '@/stores/counter'
export default defineComponent({
setup() {
return {
store: useCounterStore(),
}
},
})
Enter fullscreen mode Exit fullscreen mode
You can now access state through the store instance:
// components/MyCounter.vue
<template>
<p>Counter: {{ ERROR }}</p>
</template>
Enter full screen mode Exit fullscreen mode
#
2. Computed properties
To write cleaner code, you may wish to grab specific properties. However, destructuring the store will break reactivity.
Instead, we can use a computed property to achieve reactivity:
// components/MyCounter.vue
export default defineComponent({
setup() {
const store = useCounterStore()
// ❌ Bad (unreactive):
const { count } = store
// ✔️ Good:
const count = computed(() => store.count)
return { count }
},
})
Enter fullscreen mode Exit fullscreen mode
// components/MyCounter.vue
<template>
<p>Counter: {{ ERROR }}</p>
</template>
Enter fullscreen mode Exit fullscreen mode
#
3. Extract via storeToRefs()
You can destructure properties from the store while keeping reactivity through the use of storeToRefs()
.
This will create a ref for each reactive property.
// components/MyCounter.vue
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'
export default defineComponent({
setup() {
const store = useCounterStore()
// ❌ Bad (unreactive):
const { count } = store
// ✔️ Good:
const { count } = storeToRefs(store)
return { count }
},
})
Enter fullscreen mode Exit fullscreen mode
// components/MyCounter.vue
<template>
<p>Counter: {{ ERROR }}</p>
</template>
Enter fullscreen mode Exit fullscreen mode
#
Actions
#
Adding an action
Actions are the equivalent of methods
in components, defined in the store's actions
property.
// stores/counter.ts
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
actions: {
increment() {
this.count++
},
},
})
#
Using an action
In your component, extract the action from the store.
// components/MyCounter.vue
export default defineComponent({
setup() {
const store = useCounterStore()
const { increment } = store
const count = computed(() => store.count)
return { increment, count }
},
})
The action can easily be invoked, such as upon a button being clicked:
// components/MyCounter.vue
<template>
<button type="button" @click="increment"></button>
</template>
#
Getters
Getters are the equivalent of computed
in components, defined in the store's getters
property.
#
Adding a getter
Pinia encourages the usage of the arrow function, using the state as the first parameter:
// stores/counter.ts
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
getters: {
getCount: (state) => state.count,
},
actions: {
increment() {
this.count++
},
},
})
#
Using a getter
Similarly to state properties, getters need to be accessed in a way that maintains reactivity.
For instance, you could access it through the store instance:
// components/MyCounter.vue
export default defineComponent({
setup() {
const store = useCounterStore()
return { store }
},
})
// components/MyCounter.vue
<template>
<p>Counter: {{ ERROR }}</p>
</template>
Or, by using a computed property:
// components/MyCounter.vue
export default defineComponent({
setup() {
const store = useCounterStore()
// ❌ Bad (unreactive):
const { getCount } = store
// ✔️ Good:
const getCount = computed(() => store.getCount)
return { getCount }
},
})
// components/MyCounter.vue
<template>
<p>Counter: </p>
</template>
Or, by using storeToRefs()
:
// components/MyCounter.vue
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'
export default defineComponent({
setup() {
const store = useCounterStore()
// ❌ Bad (unreactive):
const { getCount } = store
// ✔️ Good:
const { getCount } = storeToRefs(store)
return { getCount }
},
})
// components/MyCounter.vue
<template>
<p>Counter: </p>
</template>
#
A complete component
Since we've discussed actions and getters separately, here is a code snippet that combines both in the style that I recommend:
// components/MyCounter.vue
import { useCounterStore } from '@/stores/counter'
export default defineComponent({
setup() {
const store = useCounterStore()
const getCount = computed(() => store.getCount)
const { increment } = store
return { getCount, increment }
},
})
Enter fullscreen mode Exit fullscreen mode
// components/MyCounter.vue
<template>
<p>Counter: </p>
<button type="button" @click="increment">Increment</button>
</template>
This code has been implemented at lloydtao/nuxt-3-starter/: