Transition Flow - Lifecycle Hooks
Every machine in matchina runs the same transition pipeline. FactoryMachine, StateMachine, and StoreMachine all share one generic EventLifecycle, so once you know the steps for one, you know them for all three.
The table below walks a single call through machine.send(type, ...params) from entry to completion. If you’re using a typed api wrapper, that’s where things start; otherwise, step 1 is the first frame.
| Hook | Description | Returns |
|---|---|---|
api[type](...params)optional | Model may call machine.send(type, …params). | void |
send(type, ...params) | Resolve the event and transition | void |
resolveExit(ev) | Resolve the change event with its to state.return undefined to exit | E | undefined |
transition(ev) | Run the transition lifecycle pipeline. | E | undefined |
guard(ev) | Is the change allowed?return false to halt | boolean |
handle(ev) | Map trigger event to change event.return undefined to halt | E | undefined |
before(ev) | Last chance to abort or rewrite.return undefined to halt | E | undefined |
update(ev) | Apply the new state. | void |
effect(ev) | Run side effects. | void |
leave(ev) | What happens as we exit the old state? | void |
enter(ev) | What happens as we land in the new state? | void |
notify(ev) | Tell subscribers what happened. | void |
after(ev) | Final hook — transition is done. | void |
Step-by-step
Section titled “Step-by-step”0. api[type](...params) (optional)
Section titled “0. api[type](...params) (optional)”A typed surface over the machine, so your call sites read like api.checkout(item) instead of send("checkout", item). Matchina ships factories for generating these wrappers (see Machine Enhancers). They delegate straight to machine.send and don’t participate in the lifecycle themselves. Hooks are still the usual way to extend a machine; wrappers are there when you want the ergonomics.
1. send
Section titled “1. send”send is the idiomatic way to mutate a matchina machine. A “pure” machine (see the pure enhancer) exposes exactly { getState(), send(type, ...params) }. Everything else is layered on top: typed api wrappers, hooks, subscriptions. They all flow through this one call.
send itself sits outside the lifecycle. It resolves the change event, then hands off to the transition pipeline below.
send(type, ...params) { const resolved = machine.resolveExit({ type, params, from: lastChange.to }); if (resolved) machine.transition(resolved);}If a generated API method like api.doSomething(a, b) is present, it sits one frame above and forwards into send.
2. resolveExit
Section titled “2. resolveExit”Looks up the transition handler for the current state and event type, then returns the resolved to state on the event { type, from, to? }.
- Return
nullorundefinedand the pipeline never starts. No hooks run.
3. transition
Section titled “3. transition”The pipeline itself. The steps below run in order against the resolved event. Three of them (guard, handle, before) can short-circuit the rest.
4. guard
Section titled “4. guard”Should this transition be allowed at all?
- Return
falseand the pipeline exits. Nothing changes.
5. handle
Section titled “5. handle”First place you can inspect or rewrite the event before it commits.
- Return
undefinedand the pipeline exits.
6. before
Section titled “6. before”Last chance to abort or rewrite the event. After this, the state change is real.
- Return
undefinedand the pipeline exits.
7. update
Section titled “7. update”Commits the new state. From here on, hooks are observing, not deciding.
8. effect
Section titled “8. effect”Runs side effects for the transition. By default this calls leave then enter.
9. leave
Section titled “9. leave”Fires as the previous state is exited. Good place for cleanup tied to the old state.
10. enter
Section titled “10. enter”Fires as the new state is entered. Good place for setup tied to the new state.
11. notify
Section titled “11. notify”Tells subscribers the transition happened.
12. after
Section titled “12. after”Final hook. The transition is fully done; this is where anything that needs to run “once everything settled” goes.
Early exits
Section titled “Early exits”Three things can stop the flow before update runs:
resolveExitfinds no matching transition (returnsnullorundefined)guardreturnsfalsehandleorbeforereturnsundefined
When any of those happen, no later steps run and no state change occurs.