ChartInstance
A ChartInstance is created by calling chart.start(). It represents a running statechart that can receive events and emit state changes.
Creating an Instance
Section titled “Creating an Instance”import { chart } from "statecharts.sh";
const toggle = chart({ context: { enabled: false }, initial: "off", states: { off: { on: { TOGGLE: "on" } }, on: { on: { TOGGLE: "off" } }, },});
// Start with default contextconst instance = toggle.start();
// Or override initial contextconst instance = toggle.start({ enabled: true });Properties
Section titled “Properties”The current state snapshot.
interface IStateSnapshot<TContext> { value: StateValue; // Current state value context: Readonly<TContext>; // Current context done: boolean; // True if in final state path: readonly string[]; // Full state path timestamp: number; // When snapshot was created matches(value: string): boolean; // Check if in state}Example:
const instance = toggle.start();
console.log(instance.state.value); // "off"console.log(instance.state.context); // { enabled: false }console.log(instance.state.done); // falseconsole.log(instance.state.matches("off")); // trueMethods
Section titled “Methods”send(event)
Section titled “send(event)”Send an event to trigger a transition.
// Send event by type stringinstance.send("TOGGLE");
// Send event with payloadinstance.send({ type: "SET_VALUE", value: 42 });subscribe(listener)
Section titled “subscribe(listener)”Subscribe to state changes. Returns an unsubscribe function.
const unsubscribe = instance.subscribe((state) => { console.log("State changed:", state.value); console.log("Context:", state.context);});
instance.send("TOGGLE"); // triggers listener
unsubscribe(); // stop listeningonTransition(listener)
Section titled “onTransition(listener)”Listen to events being sent. Useful for debugging or logging.
const unsubscribe = instance.onTransition((event) => { console.log("Event sent:", event.type);});stop()
Section titled “stop()”Stop the instance and clean up resources (timers, invocations).
instance.stop();
// After stopping, send() has no effectinstance.send("TOGGLE"); // ignoredState Value
Section titled “State Value”The state.value property represents the current state:
// Simple stateinstance.state.value // "loading"
// Nested stateinstance.state.value // { authenticated: "idle" }
// Use path for full hierarchyinstance.state.path // ["authenticated", "idle"]Matching States
Section titled “Matching States”Use matches() to check current state:
if (instance.state.matches("loading")) { // Show spinner}
// For nested states, check any levelconst nested = chart({ context: {}, initial: "auth", states: { auth: { initial: "login", states: { login: {}, register: {}, }, }, },});
const inst = nested.start();inst.state.matches("auth"); // trueinst.state.matches("auth.login"); // true (if at login)Example: React Integration
Section titled “Example: React Integration”import { useState, useEffect } from "react";import { chart } from "statecharts.sh";
const fetchMachine = chart({ context: { data: null, error: null }, initial: "idle", states: { idle: { on: { FETCH: "loading" } }, loading: { invoke: async () => { const res = await fetch("/api/data"); return res.json(); }, onDone: { target: "success", action: (ctx, e) => ({ data: e.data }), }, onError: { target: "error", action: (ctx, e) => ({ error: e.error }), }, }, success: { on: { RETRY: "loading" } }, error: { on: { RETRY: "loading" } }, },});
function useMachine() { const [state, setState] = useState(() => fetchMachine.start().state); const [instance] = useState(() => fetchMachine.start());
useEffect(() => { const unsub = instance.subscribe(setState); return () => { unsub(); instance.stop(); }; }, [instance]);
return [state, instance.send.bind(instance)] as const;}