Skip to content

Instantly share code, notes, and snippets.

@madmod
Last active January 30, 2020 20:32
Show Gist options
  • Save madmod/3eec59ae4aeddae1ddd2138c2474270b to your computer and use it in GitHub Desktop.
Save madmod/3eec59ae4aeddae1ddd2138c2474270b to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
const guards = {
isPhoneConfirmed(ctx, event) {
return !!(ctx && ctx.phone && ctx.phoneConfirmed)
}
}
const services = {
getRewards: async () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(['free stuff'])
}, 1000)
})
}
}
/*
* Questions:
* - How can I skip a state based on a condition, without duplicating the transition in the events. (See ConfirmingPhone)
* - How can I do an async request and send different events (to the current state) depending on the response? (See: CheckingRewards)
* - How can I forward an event to another state? (See: ShowingRewards PHONE_ENTERED_BY_CASHIER)
*/
const ExampleMachine = Machine(
{
id: "example",
initial: "WelcomingCustomer",
context: {
phone: null,
phoneConfirmed: null,
rewards: null
},
states: {
// Either the customer or the cashier enters a phone number. If the cashier enters it then the customer must confirm it.
WelcomingCustomer: {
on: {
PHONE_ENTERED_BY_CUSTOMER: {
target: "ConfirmingPhone",
// Normally this would go directly to CheckingRewards but for the example it goes to ConfirmingPhone to show the skip isn't working.
// target: "CheckingRewards"
actions: assign({
phone: (ctx, e = {}) => (e.phone || null),
phoneConfirmed: true
})
},
PHONE_ENTERED_BY_CASHIER: {
target: "ConfirmingPhone",
actions: assign({
phone: (ctx, e = {}) => (e.phone || null),
})
}
}
},
// The customer confirms or rejects the phone number entered by the cashier.
ConfirmingPhone: {
on: {
// Skip if the customer phone is already confirmed.
"": {
cond: "isPhoneConfirmed",
actions: send("PHONE_CONFIRMED"),
internal: true
},
// User confirms phone.
PHONE_CONFIRMED: "CheckingRewards",
// User says phone is incorrect.
PHONE_INCORRECT: "WelcomingCustomer"
}
},
// The customer rewards are requested from the api.
CheckingRewards: {
invoke: {
src: "getRewards",
onDone: [
{
actions: assign({ rewards: (ctx, event) => event.data })
},
{
cond: "hasRewards",
action: send("HAS_REWARDS")
// target: "ShowingRewards"
},
{
action: send("NO_REWARDS")
// target: "WelcomingCustomer"
}
],
onError: "WelcomingCustomer"
},
on: {
HAS_REWARDS: "ShowingRewards",
// Really this goes somewhere else but it is simplified for the example.
NO_REWARDS: "WelcomingCustomer",
// The cashier can enter a new phone at any point.
PHONE_ENTERED_BY_CASHIER: {
action: (ctx, event) => send(event, { to: "WelcomingCustomer" })
}
}
},
ShowingRewards: {
on: {
// The cashier can enter a new phone at any point.
PHONE_ENTERED_BY_CASHIER: {
action: (ctx, event) => send(event, { to: "WelcomingCustomer" })
}
}
}
}
},
{
guards,
services
}
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment