v0.7.1 (build 925)

Import #

import Store from '/rom/std/store.js';

Overview #

Store is a reactive state container. You read and write properties on the store as if it were a plain object, and subscribers are notified automatically — even for deeply nested changes.

Under the hood, Store wraps its data in ES Proxy objects that intercept every get and set. Writes trigger callbacks registered via subscribe(), using dot-delimited paths to identify which part of the state changed.

Quick example #

import Store from '/rom/std/store.js';

const store = new Store({
    page: 'home',
    user: { name: { first: 'Alice', last: 'Smith' } }
});

// Subscribe to a top-level key
const off = store.subscribe('page', (val) => {
    console.log('page changed to:', val);
});
// Fires immediately with current value: 'home'

// Assign like a normal object
store.page = 'settings';       // subscriber fires: 'settings'

// Deep changes are tracked
store.user.name.first = 'Bob'; // fires 'user.name.first' subscribers

// Unsubscribe when done
off();

Constructor #

new Store
Creates a new store pre-populated with initial data. The returned object is a Proxy — you can read and write data keys directly on it.
new Store(initial?: object): Store

Methods #

subscribe
Registers callback to be called whenever the value at path changes. Returns an unsubscribe function.
  • path : string — Dot-delimited path, e.g. 'user.name.first'.
  • callback : (value) => void — Invoked with the new value on change.
  • immediate : boolean — If true (the default), the callback fires immediately with the current value.
store.subscribe(path: string, callback: (value): void, immediate?: boolean): () : void
get
Reads the value at a dot-delimited path. Equivalent to traversing the object manually, but safe if intermediate keys are null or undefined.
store.get(path: string): any

Reactive writes #

Every property write — at any depth — fires subscribers whose path matches the write path exactly. Writes are detected through ES Proxy traps, so no special setter functions are needed.

const store = new Store({ config: { theme: 'dark', fontSize: 14 } });

store.subscribe('config.theme', (theme) => {
    console.log('theme:', theme);
});

store.config.theme = 'light'; // subscriber fires with 'light'
Note

Subscribers are matched by exact path. A subscriber on 'config' will not fire when 'config.theme' changes; subscribe to the specific path you care about.

Common patterns #

Binding UI to state #

Subscribe to store paths and update Koya elements when values change. This keeps rendering logic reactive without manual polling.

import { Window } from '/rom/std/koya-ui.js';
import Store from '/rom/std/store.js';

const win = await Window.create({ role: 'window', size: { x: 300, y: 100 } });
const label = await win.createElement({
    renderable: { type: 'text', size: 16, colour: [1, 1, 1, 1] }
});
await win.attach(label);

const store = new Store({ greeting: 'Hello, world!' });

store.subscribe('greeting', async (val) => {
    await label.setTextString(val);
    await win.render();
});

// Later…
store.greeting = 'Welcome back!';

Use a store key to represent the active page and subscribe to it for view switching.

const store = new Store({ page: 'home' });

store.subscribe('page', (page) => {
    switch (page) {
        case 'home':     showHome(); break;
        case 'settings': showSettings(); break;
    }
});

// Navigate
store.page = 'settings';