Skip to content

Side-effects

Side-effects allow your state-machines to interact with the outside world.

In yay-machine you can optionally run side-effects.

  • when the machine is started or stopped
  • when a state is entered or exited
  • during a transition

Side-effects are just functions

In yay-machine side-effects are just functions.

The side-effect function MAY return a cleanup function, which you can use to free resources (eg, clear timers, release connections, etc).

type Cleanup = () => void;
type SideEffect = (param: {
/* ... */
}) => Cleanup | null | undefined | void;

Side-effect lifetime

The lifetime of a side-effect matches the lifetime of where it is defined.

More concretely

  • a machine onStart() side-effect MAY run as long as the machine is running (not stopped). When the machine is stopped, the machine’s onStart() side-effect is cleaned-up (the returned cleanup function (if any) is called, and the side-effect may no longer send() the machine events)
  • a state onEnter() side-effect MAY run as long as the machine remains in that state. When the machine exits that state via a transition (or the machine is stopped), the state’s onEnter() side-effect is cleaned-up (the returned cleanup function (if any) is called, and the side-effect may no longer send() the machine events)
  • a transition onTransition() side-effect is transient, since it only exists for the transition, so it is cleaned-up immediately
  • a state onExit() side-effect is transient, since it only exists while exiting the state, so it is cleaned-up immediately
  • a machine onStop() side-effect is transient, since it only exists while stopping the machine, so it is cleaned-up immediately

Machine onStart(), onStop()

These two optional side-effects are run when the machine is started/stopped

const machine = defineMachine<State, Event>({
onStart: ({ state, send }) => {
/* ... */
},
onStop: ({ state }) => {
/* ... */
},
// ...
});

They receive a single parameter containing the machine’s current state and a send() function, which can be used to send events to the machine instance.

State onEnter(), onExit()

These two optional side-effects are run when a specific state is entered or exited

const machine = defineMachine<State, Event>({
states: {
[stateName]: {
onEnter: ({ state, event, send }) => {
/* ... */
},
onExit: ({ state, event, send }) => {
/* ... */
},
},
},
// ...
});

Like machine-lifecycle side-effects, they receive a single parameter containing the machine’s current state and a send() function, which can be used to send events to the machine instance. Depending on what triggered the state-entry, they may receive an event too.

Transition onTransition()

This optional side-effect is run during a transition from a specific state or any state

const machine = defineMachine<State, Event>({
states: {
[stateName]: {
on: {
[EVENT_TYPE]: {
to: "nextStateName",
onTransition: ({ state, event, next, send }) => {
/* ... */
},
},
},
},
},
// ...
});

And/or

const machine = defineMachine<State, Event>({
on: {
[EVENT_TYPE]: {
to: "nextStateName",
onTransition: ({ state, event, send }) => {
/* ... */
},
},
},
// ...
});

Transition side-effects receive a single parameter containing the machine’s current state, the current event, and a send() function, which can be used to send events to the machine instance.