Action adalah tempat logika mutasi dan efek samping di Pinia. Episode ini membahas action dasar, action async yang memanggil API dengan status loading dan error, serta action yang memanggil store lain. Kalian juga belajar pola reusability agar action bisa dipanggil dari komponen mana pun.

State menyimpan data, getters menghitung turunan, dan actions membawa data ke dalam store. Action adalah method yang boleh mengubah state, memanggil API, mengatur timer, atau berkoordinasi dengan store lain — semua efek samping hidup di sini, bukan di komponen.
Episode 7 membahas action dari dasar sampai pola async production: action sinkron sederhana, action async dengan status loading dan error, serta action yang memanggil store lain. Di akhir episode, kalian akan punya pola action yang bisa dipakai komponen mana pun tanpa duplikasi logika.
Action dasar adalah method yang mengubah state. Di Options store, gunakan this; di Setup store, gunakan closure:
export const useCartStore = defineStore('cart', {
state: () => ({
items: [] as { id: number; name: string; qty: number }[],
}),
actions: {
addItem(item: { id: number; name: string }) {
const existing = this.items.find((i) => i.id === item.id)
if (existing) {
existing.qty++
} else {
this.items.push({ ...item, qty: 1 })
}
},
clear() {
this.items = []
},
},
})addItem(item) menerapkan logika bisnis (jika sudah ada, tambah qty; jika belum, push baru) dalam satu tempat. Komponen cukup memanggil store.addItem(item) — tidak ada duplikasi logika di setiap tombol.
Saat action memanggil API, simpan status loading dan error di state agar komponen bisa merender sesuai kondisi:
export const useUserStore = defineStore('user', {
state: () => ({
profile: null as { name: string } | null,
loading: false,
error: null as string | null,
}),
actions: {
async fetchProfile() {
this.loading = true
this.error = null
try {
const res = await fetch('/api/profile')
if (!res.ok) throw new Error('Gagal memuat profil')
this.profile = await res.json()
} catch (e) {
this.error = e instanceof Error ? e.message : 'Terjadi kesalahan'
} finally {
this.loading = false
}
},
},
})Pola loading, error, lalu finally adalah standar untuk semua action async. this.loading = true di awal dan this.loading = false di finally menjamin status selalu benar, termasuk saat terjadi error.
Action boleh berkoordinasi dengan store lain lewat useXStore():
import { useAuthStore } from '@/stores/auth'
export const useCheckoutStore = defineStore('checkout', {
state: () => ({ status: 'idle' as 'idle' | 'success' | 'error' }),
actions: {
async checkout() {
const authStore = useAuthStore()
if (!authStore.isLoggedIn) {
await authStore.redirectToLogin()
return
}
this.status = 'success'
},
},
})useAuthStore() di dalam action memastikan store auth diaktifkan saat dibutuhkan. Ini adalah pola inti untuk composing stores, yang akan dibahas lebih dalam di episode 12.
Action async mengembalikan Promise, jadi komponen bisa menunggu hasilnya dan menangani sukses atau gagal:
<script setup lang="ts">
import { useUserStore } from '@/stores/user'
const store = useUserStore()
async function muat() {
await store.fetchProfile()
if (store.error) {
console.error(store.error)
}
}
</script>await store.fetchProfile() membuat komponen menunggu hingga action selesai. Action dipakai juga sebagai source of truth untuk status, sehingga UI bisa menampilkan spinner saat loading true dan pesan error saat error terisi.
Warning
Jangan panggil API langsung di komponen lalu menulis hasilnya ke store dengan store.$patch. Taruh seluruh alur di action — ini memudahkan testing (episode 17) dan menghindari logika tersebar.
Episode 7 menutup segitiga dasar Pinia dengan actions. Kalian sekarang bisa menulis action sinkron dan async, mengelola status loading dan error, memanggil store lain dari dalam action, dan memakai action async dari komponen dengan await.
Inti yang harus dibawa pulang:
loading dan error di state.loading true di awal action.finally menjamin loading mati di semua jalur.useXStore().await untuk mengikuti hasilnya.Di episode 8 selanjutnya kita akan membahas plugin system Pinia — cara menambah properti global ke semua store, menerima opsi plugin, serta memakai $subscribe dan $onAction untuk observability. Ini membuka jalan untuk fitur seperti persistence di episode 9.