Last active
January 30, 2020 20:32
-
-
Save madmod/3eec59ae4aeddae1ddd2138c2474270b to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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