State

A reactive state management system with subscriptions, computed values, batched updates, Redux-style actions, local-storage persistence, and selector functions. Access it via Aura.state.

Quick Start

Initialize default state, subscribe to changes, and update values reactively.

js
import Aura from 'aurajs';

// Set defaults (merged with any persisted data)
Aura.state.init({
  count: 0,
  user: null,
  theme: 'dark'
});

// Subscribe to a key
Aura.state.subscribe('count', (newVal, oldVal) => {
  console.log(`count: ${oldVal} -> ${newVal}`);
});

// Update the value
Aura.state.set('count', 1);
// Logs: "count: 0 -> 1"

init() core

Initialize the state store with default values. If previously persisted data exists in localStorage, it is merged on top of the defaults (persisted values take priority).

Aura.state.init(defaults: Record<string, unknown>): void
ParamTypeDescription
defaultsRecord<string, unknown>An object of key-value pairs to use as the initial state.
js
Aura.state.init({
  todos: [],
  filter: 'all',
  darkMode: true
});

get() core

Retrieve the current value for a given key. If a computed() value is registered under the same name, the computed selector runs instead. Returns undefined if the key does not exist.

Aura.state.get<T = unknown>(key: string): T | undefined
ParamTypeDescription
keystringThe state key to look up.
js
const count = Aura.state.get('count');       // number | undefined
const user  = Aura.state.get<User>('user');  // User | undefined (TS)

set() core

Set a value in the store. If the new value is strictly equal (===) to the current value, the update is skipped. Otherwise, emits state:<key> and state:change events (unless inside a batch()).

Aura.state.set(key: string, value: unknown): void
ParamTypeDescription
keystringThe state key to update.
valueunknownThe new value to store.
js
Aura.state.set('count', 42);
Aura.state.set('user', { name: 'Alice', role: 'admin' });

getAll() utility

Return a shallow copy of the entire state object. Useful for debugging or serialization.

Aura.state.getAll(): Record<string, unknown>

This method takes no parameters.

js
const snapshot = Aura.state.getAll();
console.log(snapshot); // { count: 42, user: {...}, theme: 'dark' }

subscribe() reactive

Listen for changes to a specific state key. The callback receives the new and old values. Returns an unsubscribe function for easy cleanup.

Aura.state.subscribe(key: string, fn: (newVal: unknown, oldVal: unknown) => void): () => void
ParamTypeDescription
keystringThe state key to watch.
fnFunctionCallback invoked with (newVal, oldVal) whenever the key changes.

Returns a () => void unsubscribe function.

js
const unsub = Aura.state.subscribe('theme', (newVal, oldVal) => {
  document.body.className = newVal;
});

// Later, stop listening
unsub();

computed() reactive

Register a derived value that is computed from the full state. When you call get(name), the selector runs against the current state instead of performing a direct lookup.

Aura.state.computed(name: string, selector: (state: Record<string, unknown>) => unknown): void
ParamTypeDescription
namestringThe virtual key name for this computed value.
selectorSelectorA function (state) => derivedValue that computes the value from the full state.
js
Aura.state.computed('activeTodos', (state) => {
  return state.todos.filter(t => !t.done);
});

// Now use it like any other key
const active = Aura.state.get('activeTodos');
console.log(active.length);

batch() performance

Group multiple set() calls into a single batch. Individual state:<key> events are deferred until the batch function completes, then fired all at once. A single state:batch event is also emitted with all changes.

Aura.state.batch(fn: () => void): void
ParamTypeDescription
fnFunctionA synchronous function that calls set() one or more times.
js
Aura.state.batch(() => {
  Aura.state.set('loading', false);
  Aura.state.set('user', userData);
  Aura.state.set('token', jwt);
});
// All three state:<key> events fire here, after the batch
// Plus one state:batch event with all changes

action() advanced

Register a named action (reducer-style). Actions receive the full current state and an optional payload, and return a new state object. Changed keys are automatically updated via set().

Aura.state.action(name: string, handler: (state: Record<string, unknown>, payload?: unknown) => Record<string, unknown>): void
ParamTypeDescription
namestringUnique action name used with dispatch().
handlerActionFunction (state, payload?) => newState. Return the full new state object.
js
Aura.state.action('increment', (state, amount) => ({
  ...state,
  count: state.count + (amount ?? 1)
}));

Aura.state.action('addTodo', (state, text) => ({
  ...state,
  todos: [...state.todos, { text, done: false }]
}));

dispatch() advanced

Execute a registered action by name. Also supports a built-in 'fetch' action that fetches JSON from a URL and stores it under a key. Throws if the action name is not found.

Aura.state.dispatch(actionName: string, payload?: unknown): Promise<void>
ParamTypeDescription
actionNamestringName of a registered action, or the built-in 'fetch'.
payloadunknownData passed to the action handler. For 'fetch', pass { url, key }.
js
// Dispatch a custom action
await Aura.state.dispatch('increment', 5);
console.log(Aura.state.get('count')); // previous + 5

// Built-in fetch action
await Aura.state.dispatch('fetch', {
  url: '/api/users',
  key: 'users'
});
// Aura.state.get('users') now has the fetched JSON

select() utility

Run an ad-hoc selector function against the current state and return the result. Unlike computed(), this does not register anything -- it is a one-shot query.

Aura.state.select<T>(selector: (state: Record<string, unknown>) => T): T
ParamTypeDescription
selectorSelector<T>A function that receives the full state and returns a derived value.
js
const total = Aura.state.select(s => s.todos.length);
const admin = Aura.state.select(s => s.user?.role === 'admin');

persist() utility

Save the entire current state to localStorage under the key app_state. On the next init() call, persisted data is automatically restored and merged with defaults.

Aura.state.persist(): void

This method takes no parameters.

js
// Save state before the user leaves
window.addEventListener('beforeunload', () => {
  Aura.state.persist();
});

reset() destructive

Clear all state and optionally replace it with new defaults. Emits state:<key> for every key whose value changed, plus a state:reset event with the new state.

Aura.state.reset(defaults?: Record<string, unknown>): void
ParamTypeDescription
defaultsRecord<string, unknown>Optional new state. If omitted, state is set to an empty object {}.
js
// Reset to empty
Aura.state.reset();

// Reset with new defaults
Aura.state.reset({ count: 0, theme: 'light' });

Emitted Events

The state module emits events via the Aura event bus. Subscribe with Aura.on() or Aura.state.subscribe().

EventPayloadDescription
state:<key>newVal, oldValFired when a specific key changes via set().
state:changekey, newVal, oldValFired on every individual key change (outside of batch).
state:batch[key, val, old][]Fired after a batch() completes with all changes.
state:resetnewStateFired when reset() is called.
js
// Listen to all state changes
Aura.on('state:change', (key, newVal, oldVal) => {
  console.log(`${key} changed`, oldVal, '->', newVal);
});

// Listen to batch completions
Aura.on('state:batch', (changes) => {
  console.log(`${changes.length} keys updated in batch`);
});