Skip to content

Instantly share code, notes, and snippets.

@ilumin
Last active December 3, 2020 08:41
Show Gist options
  • Save ilumin/97b7b9f52aed865322c6cb79f44bc6a6 to your computer and use it in GitHub Desktop.
Save ilumin/97b7b9f52aed865322c6cb79f44bc6a6 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
/**
defined: actions in config
note:
- states: contain state of machine
- on: contain event support on state
- event can define as string of state name or an object of target state and action
- action can do side effect
# Mutate context by assign
*/
const config = {
id: 'lightBulb',
initial: 'unlit',
context: {
color: '#FFF',
},
states: {
lit: {
on: {
BREAK: 'broken',
TOGGLE: 'unlit',
CHANGE_COLOR: {
actions: ['changeColor']
}
},
exit: ['logChange'],
},
unlit: {
on: {
BREAK: 'broken',
TOGGLE: 'lit',
},
exit: ['logChange'],
},
broken: {
entry: ['logBroken']
},
}
}
const options = {
actions: {
logBroken: (ctx, evt) => console.log(`I'm broke at ${evt.location || `somewhere`}.`),
logChange: (ctx, evt) => console.log(`changed: `, ctx, evt),
changeColor: assign((ctx, evt) => ({ color: evt.color || 'no-color-here' }))
}
}
const lightBulbMachine = Machine(
config,
options
);
/**
* Hierarchical state can transfer to extenal state by using id of state
*/
const doorMachine = Machine(
{
id: 'door',
initial: 'locked',
states: {
locked: {
id: 'locked',
on: {
UNLOCK: 'unlocked',
}
},
unlocked: {
initial: 'closed',
states: {
closed: {
on: {
LOCK: '#locked',
OPEN: 'opened'
}
},
opened: {
on: {
CLOSE: 'closed'
}
}
}
},
}
}
)
/**
* XState support parallel state transition
*/
const spaceHeaterMachine = Machine(
{
id: 'spaceHeater',
initial: 'powerOff',
states: {
powerOff: {
on: { TOGGLE: 'powerOn.hist' }
},
powerOn: {
on: { TOGGLE: 'powerOff' },
type: 'parallel',
states: {
heated: {
initial: 'lowHeat',
states: {
lowHeat: {
on: { TOGGLE_HEAT: 'highHeat' }
},
highHeat: {
on: { TOGGLE_HEAT: 'lowHeat' }
}
}
},
ascillation: {
initial: 'disabled',
states: {
disabled: {
on: { TOGGLE_OSC: 'enabled' }
},
enabled: {
on: { TOGGLE_OSC: 'disabled' }
},
}
},
hist: {
type: 'history',
history: 'deep',
}
}
},
}
}
);
const tryMachine = Machine(
{
id: 'tryMachine',
context: {
tries: 0
},
initial: 'idle',
states: {
idle: {
on: { TRYING: 'trying' }
},
trying: {
entry: ['incTries'],
on: {
'': [
{ target: 'success', cond: 'triedEnough' },
{ target: 'idle' }
]
}
},
success: {}
}
},
{
actions: {
incTries: assign({
tries: ctx => ctx.tries + 1
})
},
guards: {
triedEnough: ctx => ctx.tries > 2
}
}
)
/**
* Example use case traffic light
* 1. traffice light should run automatically with delayed time
* 2. delay time can defined as `delays` in options and defined with `after`
* 3. example of machine event
* 3.1 machine event can trigger target state
* 3.2 trigger to previous state
* 3.3 add condition so that it won't trigger if not met condition
*/
const stoplightMachine = Machine({
id: 'stoplight',
initial: 'red',
context: {
rushHourMultiplier: 1
},
on: {
INC_RUSH_HOUR: {
target: '#stoplight.hist',
cond: 'maxModifier',
actions: ['incRushHour']
},
DEC_RUSH_HOUR: {
target: '#stoplight.hist',
cond: 'minModifier',
actions: ['decRushHour']
}
},
states: {
green: {
after: {
GREEN_TIMER: 'yellow'
}
},
yellow: {
after: {
YELLOW_TIMER: 'red'
}
},
red: {
after: {
RED_TIMER: 'green'
}
},
hist: {
type: 'history'
}
},
},
{
actions: {
incRushHour: assign({
rushHourMultiplier: ctx => ctx.rushHourMultiplier + 1
}),
decRushHour: assign({
rushHourMultiplier: ctx => ctx.rushHourMultiplier - 1
})
},
delays: {
GREEN_TIMER: ctx => ctx.rushHourMultiplier * 3000,
YELLOW_TIMER: ctx => ctx.rushHourMultiplier * 1000,
RED_TIMER: ctx => ctx.rushHourMultiplier * 4000
},
guards: {
minModifier: ctx => ctx.rushHourMultiplier > 1,
maxModifier: ctx => ctx.rushHourMultiplier < 5,
}
})
const fetchCureAnimals = () => {
return fetch(`https://www.reddit.com/r/aww.json`)
.then(res => res.json())
.then(json => json.data.children.map(child => child.data))
}
/**
* invoke promise function
* instead of use `on` just use `invoke`
* and defined promise function want to trigger
* and defined onDone, onError to handle promise event
*/
const cuteAnimalsMachine = Machine(
{
id: 'cuteAnimals',
initial: 'idle',
context: {
cuteAnimals: null,
error: null,
},
states: {
idle: {
on: { FETCH: 'loading' }
},
loading: {
invoke: {
id: 'fetchCureAnimals',
src: fetchCureAnimals,
onDone: {
target: 'success',
actions: assign({
cuteAnimals: (ctx, evt) => evt.data
})
},
onError: {
target: 'failure',
actions: assign({
error: (ctx, evt) => evt.data
})
}
}
},
success: {
type: 'final'
},
failure: {
on: { RETRY: 'loading' }
},
}
}
)
/**
* invoke callback event
* by define invoke
* and send event to callback function
*/
const callbackEcho = (ctx, evt) => (callback, onEvent) => onEvent(e => e.type === 'HEAR' && callback('ECHO'))
const listingMachine = Machine(
{
id: 'listening',
initial: 'listening',
states: {
listening: {
invoke: {
id: 'callbackEcho',
src: callbackEcho
},
on: {
SPEAK_FOO: {
actions: send('FOO', {
to: 'callbackEcho'
})
},
SPEAK_HEAR: {
actions: send('HEAR', {
to: 'callbackEcho'
})
},
ECHO: {
actions: () => console.log('echo, echo')
},
}
}
}
}
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment