In order to track or perform an action when a transition occurs, five general-purpose lifecycle events can be observed:
onBeforeTransition
- fired before any transitiononLeaveState
- fired when leaving any stateonTransition
- fired during any transitiononEnterState
- fired when entering any stateonAfterTransition
- fired after any transition
In addition to the general-purpose events, transitions can be observed using your specific transition and state names:
onBefore<TRANSITION>
- fired before a specific TRANSITION beginsonLeave<STATE>
- fired when leaving a specific STATEonEnter<STATE>
- fired when entering a specific STATEonAfter<TRANSITION>
- fired after a specific TRANSITION completes
For convenience, the 2 most useful events can be shortened:
on<TRANSITION>
- convenience shorthand foronAfter<TRANSITION>
on<STATE>
- convenience shorthand foronEnter<STATE>
Observers will be passed a single argument containing a lifecycle
object with the following attributes:
- transition - the transition name
- from - the previous state
- to - the next state
In addition to the lifecycle
argument, the observer will receive any arbitrary arguments passed
into the transition method
const fsm = new StateMachine({
transitions: [{ name: 'step', from: 'A', to: 'B' }],
lifecycles: {
onTransition: function (lifecycle, arg1, arg2) {
console.log(lifecycle.transition) // 'step'
console.log(lifecycle.from) // 'A'
console.log(lifecycle.to) // 'B'
console.log(arg1) // 42
console.log(arg2) // 'hello'
},
},
})
fsm.step(42, 'hello')
Lifecycle event names always use standard javascipt camelCase, even if your transition and state names do not:
var fsm = new StateMachine({
transitions: [
{ name: 'do-with-dash', from: 'has-dash', to: 'has_underscore' },
{ name: 'do_with_underscore', from: 'has_underscore', to: 'alreadyCamelized' },
{ name: 'doAlreadyCamelized', from: 'alreadyCamelize', to: 'has-dash' },
],
lifecycles: {
onBeforeDoWithDash: function () {
/* ... */
},
onBeforeDoWithUnderscore: function () {
/* ... */
},
onBeforeDoAlreadyCamelized: function () {
/* ... */
},
onLeaveHasDash: function () {
/* ... */
},
onLeaveHasUnderscore: function () {
/* ... */
},
onLeaveAlreadyCamelized: function () {
/* ... */
},
onEnterHasDash: function () {
/* ... */
},
onEnterHasUnderscore: function () {
/* ... */
},
onEnterAlreadyCamelized: function () {
/* ... */
},
onAfterDoWithDash: function () {
/* ... */
},
onAfterDoWithUnderscore: function () {
/* ... */
},
onAfterDoAlreadyCamelized: function () {
/* ... */
},
},
})
To recap, the lifecycle of a transition occurs in the following order:
onBeforeTransition
- fired before any transitiononBefore<TRANSITION>
- fired before a specific TRANSITIONonLeaveState
- fired when leaving any stateonLeave<STATE>
- fired when leaving a specific STATEonTransition
- fired during any transitiononEnterState
- fired when entering any stateonEnter<STATE>
- fired when entering a specific STATEon<STATE>
- convenience shorthand foronEnter<STATE>
onAfterTransition
- fired after any transitiononAfter<TRANSITION>
- fired after a specific TRANSITIONon<TRANSITION>
- convenience shorthand foronAfter<TRANSITION>
Any observer can cancel a transition by explicitly returning false
during any of the following
lifecycle events:
onBeforeTransition
onBefore<TRANSITION>
onLeaveState
onLeave<STATE>
onTransition
All subsequent lifecycle events will be cancelled and the state will remain unchanged.