State Management: Kapan Butuh Lebih dari Props/Emit
Setiap kali mulai proyek Vue baru, saya hampir selalu mulai dari props dan emit saja, tanpa Pinia atau Vuex. Bukan karena anti-library, tapi karena props/emit itu eksplisit, mudah ditelusuri, dan tidak menambah lapisan abstraksi yang belum tentu dibutuhkan. Masalahnya, banyak tim (termasuk saya di masa lalu) baru sadar butuh state management terpusat setelah kodenya sudah telanjur berantakan. Padahal ada sinyal-sinyal yang bisa dikenali lebih awal, sebelum refactor jadi mahal.
Di dunia sistem informasi klinik yang saya geluti sehari-hari, layar seperti pendaftaran pasien atau input rekam medis biasanya melibatkan banyak komponen kecil yang saling bergantung: pencarian pasien, ringkasan kunjungan, form vital sign, daftar diagnosa. Di titik itulah pertanyaan "masih cukup props/emit, atau sudah waktunya state management?" jadi relevan.
Sinyal pertama: prop drilling lebih dari dua-tiga level
Props/emit bekerja baik selagi data mengalir satu-dua level antar komponen: parent ke child, child mengirim event balik ke parent. Masalah mulai muncul kalau sebuah data harus diteruskan lewat komponen perantara yang sendiri tidak memakainya, cuma jadi "kurir". Ini yang biasa disebut prop drilling.
Sebagai ilustrasi, bayangkan komponen halaman pendaftaran punya struktur begini:
<HalamanPendaftaran>
<PanelPasien :pasien="pasien" />
<RingkasanPasien :pasien="pasien" />
<BadgeAlergi :alergi="pasien.alergi" />
Kalau PanelPasien dan RingkasanPasien cuma meneruskan prop pasien tanpa benar-benar memakainya, itu tanda struktur data sebaiknya diakses langsung oleh komponen yang butuh, bukan diestafetkan berlapis-lapis. Solusinya belum tentu harus Pinia — bisa juga provide/inject untuk kasus yang masih terbatas di satu subtree komponen. Pinia baru jadi pilihan lebih tepat kalau data itu perlu diakses dari banyak subtree yang tidak berelasi langsung secara hierarki.
Sinyal kedua: state yang sama perlu disinkronkan di banyak tempat tak berelasi
Ini sinyal yang menurut saya paling jelas. Kalau dua komponen yang tidak punya hubungan parent-child langsung (misalnya komponen di sidebar dan komponen di halaman utama) sama-sama perlu tahu status yang sama — katakanlah jumlah notifikasi, atau data pasien yang sedang aktif ditangani — maka props/emit akan memaksa Anda membawa data itu naik ke komponen leluhur bersama (root, layout, dsb), lalu meneruskannya turun ke kedua cabang. Semakin jauh cabangnya, semakin banyak lapisan yang harus "tahu" data yang sebenarnya tidak mereka pakai.
Gejala praktisnya: Anda mulai emit event custom yang didengarkan oleh komponen yang "jauh", atau memakai event bus buatan sendiri lewat mitt atau semacamnya sebagai jalan pintas. Kalau sudah sampai di titik ini, itu justru sinyal state management terpusat lebih cocok, karena satu sumber kebenaran (single source of truth) menghindarkan Anda dari kebingungan "versi data yang mana yang paling baru".
Sinyal ketiga: butuh state yang bertahan lintas navigasi atau butuh reactivity lintas request
Vue Router secara default akan me-mount ulang komponen halaman saat berpindah rute. Kalau ada state yang harus tetap hidup meski pengguna pindah halaman lalu balik lagi — misalnya draft form yang sedang diisi, filter pencarian yang dipertahankan, atau data sesi login — props/emit tidak relevan lagi karena tidak ada parent yang menjembatani dua halaman yang berbeda rute.
Pinia store cocok untuk kasus ini karena instance store tetap hidup selama aplikasi berjalan, tidak tergantung siklus hidup komponen tertentu. Sebagai gambaran, store sederhana untuk sesi pengguna bisa berbentuk seperti ini:
export const useSessionStore = defineStore('session', {
state: () => ({
user: null,
filterAktif: {},
}),
actions: {
setUser(user) {
this.user = user
},
},
})
Komponen mana pun, di rute mana pun, tinggal memanggil useSessionStore() tanpa perlu tahu siapa parent-nya.
Kapan belum perlu
Sebaliknya, kalau aplikasi Anda masih berupa kumpulan form dan tabel yang relatif berdiri sendiri, dengan alur data yang jelas satu arah dari parent ke child, menambahkan Pinia justru menambah beban kognitif tanpa manfaat nyata. Saya melihat pola ini juga di proyek-proyek Laravel + Vue yang saya kerjakan: modul yang scope-nya sempit (misalnya satu halaman CRUD master data) biasanya tidak butuh store sendiri. Store baru masuk akal kalau ada state yang benar-benar dipakai bersama lintas modul, bukan sekadar "supaya rapi" atau ikut tren.
Aturan praktis yang saya pakai: mulai dari props/emit dan provide/inject untuk kasus lokal, lalu naikkan ke state management terpusat begitu Anda menemukan diri sendiri melakukan salah satu dari tiga sinyal di atas — bukan sebelum itu.
Takeaway
- Prop drilling lebih dari dua-tiga level lewat komponen yang tidak memakai datanya sendiri adalah sinyal awal.
- State yang harus sinkron antar komponen yang tidak berelasi hierarki adalah sinyal paling kuat untuk pindah ke state management terpusat.
- State yang harus bertahan lintas navigasi rute butuh store, bukan props.
- Kalau alur data masih sederhana dan satu arah, tetap di props/emit — jangan menambah kompleksitas lebih awal dari kebutuhan.