Skip to content

Context

context is the machine’s data: a plain object declared in the config and read directly off the service. Writes go through setContext, the single batched entry point.

import { machine } from '@dunky.dev/state-machine'
const m = machine({
initial: 'idle',
context: { name: 'Ada', age: 36 },
states: {
idle: {
on: {
birthday: { actions: $ => $.setContext({ age: $.context.age + 1 }) },
rename: { actions: $ => $.setContext({ name: $.event.name }) },
},
},
},
})
m.start()
m.context.name // 'Ada'
m.send({ type: 'birthday' })
m.context.age // 37

Context is one plain object per machine, copied from the config at construction. Its identity never changes; only its fields are mutated in place. This means:

  • Reads are zero-cost: no proxy, no getter.
  • Writes are batched: setContext applies a shallow merge, deduplicates unchanged fields, then notifies subscribers once.
  • A field that didn’t change wakes no observer.

Accepts a partial patch object, shallow-merged into the current context:

$.setContext({ count: 5 })

To derive the next value from the current one, read $.context (or use the act() shorthand):

$.setContext({ count: $.context.count + 1 })
// or, terser:
act($ => ({ count: $.context.count + 1 }))

setContext is available in actions, effects, and watchers via the $ argument. The machine never exposes setContext externally; m.context is read-only. To change state from outside, dispatch an event with m.send(...).

For the common case of writing context in a transition action, act() is write-sugar that drops the $ => $.setContext(...) wrapper:

import { act } from '@dunky.dev/state-machine'
on: {
increment: { actions: act($ => ({ count: $.context.count + 1 })) },
reset: { actions: act({ count: 0 }) },
}