Subscriptions
Two ways to observe a machine from the outside: coarse subscribe for any change, fine-grained select for a specific slice.
subscribe — coarse
Section titled “subscribe — coarse”Fires on any state or context change. The standard bridge for useSyncExternalStore:
const off = m.subscribe(() => rerender())off() // unsubscribeselect — fine-grained
Section titled “select — fine-grained”Narrows to a slice and fires only when that slice’s value changes. A subscriber wakes only for the data it reads:
// named field — typed and autocompletedm.select.context('count').subscribe(n => console.log('count is now', n))m.select.computed('isEmpty').subscribe(empty => toggle(empty))m.select.state().subscribe(s => console.log('state →', s))For a derived or composite slice, pass a selector function:
const view = m.select(() => ({ open: m.matches('open'), count: m.context.count,}))
// optional equality — skip re-renders when neither field changedview.subscribe(render, (a, b) => a.open === b.open && a.count === b.count)
view.value // read the current value without subscribingSelection identity
Section titled “Selection identity”A select re-evaluates on any machine change but only calls its listeners when the selected value actually changes. This makes it safe to use directly in React’s useSyncExternalStore — no infinite-loop risk from returning a fresh object on every call.
// React bridge patternconst snapshot = useSyncExternalStore( cb => m.select.context('count').subscribe(cb), () => m.context.count,)