Store
The Store class provides centralized state management for Vio applications. It holds global state, processes actions, and notifies subscribers when state changes.
Import
import { Store } from '@atrotos/vio'TIP
You typically do not create a Store directly. Instead, pass a StoreConfig to createApp and use app.dispatch() / app.getStore(). The Store class is exported for advanced use cases and testing.
Signature
class Store {
constructor(config: StoreConfig, bus: EventBus)
getState(): Record<string, unknown>
dispatch(action: string, payload?: unknown): void
subscribe(cb: StoreSubscriber): () => void
}Constructor
new Store(config: StoreConfig, bus: EventBus)| Parameter | Type | Required | Description |
|---|---|---|---|
config | StoreConfig | Yes | Store configuration with initial state and actions |
bus | EventBus | Yes | Event bus instance for emitting store events |
StoreConfig
interface StoreConfig {
state: Record<string, unknown>
actions: Record<string, StoreAction>
}
interface StoreAction {
(state: Record<string, unknown>, payload?: unknown): Record<string, unknown>
}| Property | Type | Description |
|---|---|---|
state | Record<string, unknown> | Initial state object |
actions | Record<string, StoreAction> | Map of action name to handler function. Each handler receives the current state and an optional payload, and must return the new state. |
Methods
getState()
Returns a shallow copy of the current store state.
getState(): Record<string, unknown>const state = store.getState()
console.log(state.count) // 0dispatch(action, payload?)
Dispatches a named action to mutate the store state. The action handler is called with the current state and the payload, and its return value becomes the new state.
After a dispatch:
- A
store:changeevent is emitted on the event bus withaction,payload,prev, andnextin the payload. - All subscribers are notified with the new and previous state.
dispatch(action: string, payload?: unknown): void| Parameter | Type | Required | Description |
|---|---|---|---|
action | string | Yes | Action name matching a key in StoreConfig.actions |
payload | unknown | No | Data to pass to the action handler |
store.dispatch('increment')
store.dispatch('addTodo', { text: 'Learn Vio' })WARNING
Throws Error if the action name does not match any registered action.
subscribe(cb)
Registers a callback that is invoked whenever the store state changes. Returns an unsubscribe function.
subscribe(cb: StoreSubscriber): () => voidThe subscriber callback signature:
type StoreSubscriber = (
newState: Record<string, unknown>,
prevState: Record<string, unknown>
) => void| Parameter | Type | Description |
|---|---|---|
cb | StoreSubscriber | Callback receiving new state and previous state |
const unsubscribe = store.subscribe((newState, prevState) => {
console.log('State changed from', prevState, 'to', newState)
})
// Stop listening
unsubscribe()Events Emitted
The store emits the following event on the event bus after every dispatch:
| Event | Payload |
|---|---|
store:change | { action: string, payload: Record<string, unknown>, prev: Record<string, unknown>, next: Record<string, unknown> } |
Full Example
import { createApp, defineComponent } from '@atrotos/vio'
const Counter = defineComponent({
name: 'Counter',
render: (state, store) => ({
tag: 'div',
children: [
{ tag: 'p', children: [`Global count: ${store?.count}`] },
{ tag: 'button', props: { id: 'inc' }, children: ['+'] },
{ tag: 'button', props: { id: 'dec' }, children: ['-'] }
]
})
})
const app = createApp({
root: '#app',
routes: [{ path: '/', component: Counter }],
store: {
state: { count: 0 },
actions: {
increment: (state) => ({ ...state, count: (state.count as number) + 1 }),
decrement: (state) => ({ ...state, count: (state.count as number) - 1 }),
set: (state, value) => ({ ...state, count: value })
}
}
})
app.on('store:change', (e) => {
console.log(`[${e.payload.action}]`, e.payload.prev, '->', e.payload.next)
})
app.mount()
app.dispatch('increment')
app.dispatch('set', 10)