How to Use Actions in Pinia for Vue State Management
In Pinia,
actions are methods inside a store used to change state or run async code. You define actions as functions inside the actions property of your store, then call them like regular methods from your components.Syntax
Actions are defined inside the actions object of a Pinia store. Each action is a function that can modify the store's state or perform asynchronous tasks. You call actions using the store instance.
Parts explained:
defineStore: Creates a store.state: Holds reactive data.actions: Contains methods to update state or run logic.thisinside actions: Refers to the store instance, so you can access state and other actions.
typescript
import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { state: () => ({ count: 0 }), actions: { increment() { this.count++ }, async incrementAsync() { await new Promise(resolve => setTimeout(resolve, 1000)) this.count++ } } })
Example
This example shows a Pinia store with a counter state and two actions: one to increment immediately and one to increment after a delay. The component calls these actions to update the count.
vue
import { createApp } from 'vue' import { createPinia, defineStore } from 'pinia' const useCounterStore = defineStore('counter', { state: () => ({ count: 0 }), actions: { increment() { this.count++ }, async incrementAsync() { await new Promise(resolve => setTimeout(resolve, 1000)) this.count++ } } }) const App = { template: ` <div> <p>Count: {{ counter.count }}</p> <button @click="counter.increment">Increment</button> <button @click="counter.incrementAsync">Increment Async (1s delay)</button> </div> `, setup() { const counter = useCounterStore() return { counter } } } const app = createApp(App) app.use(createPinia()) app.mount('#app')
Output
Count: 0
[Click Increment] Count: 1
[Click Increment Async] (wait 1 second) Count: 2
Common Pitfalls
Common mistakes when using actions in Pinia:
- Not using
thisinside actions to access state, causing errors. - Trying to mutate state outside actions or state property, which breaks reactivity.
- Forgetting to call async actions with
awaitif you need to wait for completion. - Defining actions outside the
actionsobject, which Pinia won't recognize.
typescript
import { defineStore } from 'pinia' // Wrong: Mutating state directly outside actions export const useWrongStore = defineStore('wrong', { state: () => ({ count: 0 }), // increment is not inside actions, so won't work increment() { this.count++ } }) // Right: Actions inside actions object export const useRightStore = defineStore('right', { state: () => ({ count: 0 }), actions: { increment() { this.count++ } } })
Quick Reference
| Concept | Description | Example |
|---|---|---|
| defineStore | Creates a Pinia store | const useStore = defineStore('id', {...}) |
| state | Reactive data inside store | state: () => ({ count: 0 }) |
| actions | Methods to change state or run logic | actions: { increment() { this.count++ } } |
| Calling actions | Use store instance methods | store.increment() |
| Async actions | Actions can be async functions | async incrementAsync() { await ...; this.count++ } |
Key Takeaways
Define actions inside the actions object to change state or run async code.
Use this inside actions to access and modify the store's state.
Call actions from components using the store instance methods.
Avoid mutating state outside actions to keep reactivity intact.
Async actions should be awaited if you need to wait for their completion.