Membedah berbagi state global dan auth antar remote: pola event bus dengan CustomEvent, central store di host yang dibagikan via props, sharing auth token yang aman, dan implementasi di storefront dengan package shared-state (zustand).

Setelah routing bekerja di episode 6, muncul kebutuhan yang tak kalah penting: remote-remote harus berbagi data — user sedang login atau tidak, role mereka apa, dan berapa item di cart. Tanpa berbagi state, tiap remote akan menyimpan duplikat data yang cepat tidak sinkron.
Mengapa penting? Karena ini titik di mana "distributed system" di frontend benar-benar terasa. Memilih pola berbagi state yang salah akan menimbulkan kebocoran memori, state yang tidak sinkron antar remote, atau token yang disimpan tidak aman. Episode ini membahas pola-pola berbagi state & auth yang sehat.
Remote butuh tahu: user login, role, dan cart count — tanpa menduplikasi state. Kalau setiap remote menyimpan salinan cart sendiri, lalu user menambah item di satu remote, remote lain tidak tahu. Solusi harus memungkinkan satu sumber data yang dipakai banyak remote.
Cara paling sederhana dan robust: memakai CustomEvent pada window sebagai bus event. Remote mempublikasikan perubahan, yang lain mendengarkan.
window.dispatchEvent(
new CustomEvent('cart:updated', { detail: { cartCount: 3 } })
)window.addEventListener('cart:updated', (e) => {
const { cartCount } = (e as CustomEvent).detail
setBadge(cartCount)
})Pattern event bus adalah tulang punggung komunikasi lintas remote (detail lanjutan di episode 18). Keunggulannya: tidak butuh dependency bersama selain kontrak nama event.
Alternatifnya: host memiliki central store (misal zustand) dan membagikan "handle" ke remote lewat props saat merender. Karena remote menerima store via props (bukan via React context lintas bundle), kita menghindari memecahkan singleton React context.
Untuk identifier semi-publik seperti cartId atau orderId, simpan di URL/query. Untuk token yang sensitif, jangan di URL — simpan aman di cookie (HttpOnly jika ada backend).
Prinsip kunci keamanan: host menyimpan session, dan remote menerima apa yang dibutuhkan via props — bukan mengakses storage host atau membuat fetch token sendiri. Ini menjaga kontrak antarmuka yang jelas antara host dan remote.
<AccountApp
user={session.user}
role={session.role}
onLogout={handleLogout}
/>Remote harus berinteraksi lewat contract via interface — props dan event — bukan menyentuh detail internal host (seperti variabel global atau storage khusus host). Ini menjaga isolasi dan memudahkan penggantian remote.
Warning
Jangan meneruskan token raw ke semua remote. Berikan remote hanya data yang dibutuhkannya (misal user dan role), bukan credential mentah. Auth untuk request API sebaiknya ditangani host atau di level gateway, bukan di tangan remote yang belum tentu tepercaya.
storefrontshared-stateKita letakkan utas state di packages/shared-state memakai zustand:
import { create } from 'zustand'
interface CartState {
cartCount: number
addToCart: (qty: number) => void
}
export const useCartStore = create<CartState>((set) => ({
cartCount: 0,
addToCart: (qty) =>
set((s) => ({ cartCount: s.cartCount + qty })),
}))Host memiliki dan membagikan store ini; remote menerimanya lewat props (bukan import langsung) agar tidak pecah singleton.
Ketika remote cart menambah item, ia mempublikasikan event, dan badge di shell rela-id:
useEffect(() => {
const handler = () => {
const n = useCartStore.getState().cartCount
setBadge(String(n))
}
window.addEventListener('cart:updated', handler)
return () => window.removeEventListener('cart:updated', handler)
}, [])Pada episode 7 ini, kalian telah memahami global state & auth antar remote.
Inti yang harus dibawa pulang:
shared-state (zustand) di host, badge cart rela-id via event cart:updated.Di episode 8 selanjutnya, kita akan membahas design system & styling bersama — package design-system, isolasi CSS (prefix, CSS Modules, CSS-in-JS, Web Components), theming dengan CSS variables, versioning semver, dan Storybook. Pastikan state kalian sudah terbagi!