Skip to content

Instantly share code, notes, and snippets.

@Krutie
Last active January 21, 2022 19:56
Show Gist options
  • Save Krutie/a25bcf359a239bffbf403ec10679d906 to your computer and use it in GitHub Desktop.
Save Krutie/a25bcf359a239bffbf403ec10679d906 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
/**
* Validate answer
* @param ctx
* @param evt
*/
const validateAnswer = (ctx, evt) =>
new Promise((resolve, reject) => {
if (evt.type === "ANSWER" && evt.answer?.selectedOption !== undefined) {
resolve(evt.answer);
} else {
reject(new Error("Please select answer."));
}
});
/* NEXT_QUESTION transition object */
const NEXT_Q = [
{
target: "answering",
cond: "canGoToNextQuestion",
actions: "goToNextQuestion",
},
{ target: "finish" },
];
/* CHECK_ANSWER transition object */
const CHECK_ANSWER = {
exit: ["clearErrorMessage"],
on: {
ANSWER: {
target: "submitting",
},
},
};
const QuizMachine = Machine(
{
id: "quiz",
initial: "initial",
context: {
currentQuestion: 0,
correct: 0,
incorrect: 0,
errorMessage: "",
answer: undefined,
totalQuestions: 0,
},
states: {
initial: {
on: {
START: {
cond: "newTotalQuestionsIsValidValue",
actions: "assignTotalQuestionsToContext",
target: "answering",
},
},
},
answering: {
initial: "idle",
id: "answering-id",
onDone: {
target: "checking",
},
states: {
idle: CHECK_ANSWER,
submitting: {
invoke: {
src: validateAnswer,
id: "validateAnswer",
onDone: {
target: "complete",
actions: assign({
answer: (context, event) => event.data,
}),
},
onError: {
target: "invalid",
actions: assign(
(context, event) => {
return {
errorMessage: event.data.message,
};
}
),
},
},
},
invalid: CHECK_ANSWER,
complete: {
type: "final",
},
},
},
checking: {
always: [
{
target: "correct",
cond: "isCorrect",
actions: assign((context) => {
return {
correct: context.correct + 1,
};
}),
},
{
target: "incorrect",
cond: "isIncorrect",
actions: assign((context) => {
return {
incorrect: context.incorrect + 1,
};
}),
},
],
},
correct: {
on: {
NEXT_QUESTION: NEXT_Q,
},
},
incorrect: {
on: {
NEXT_QUESTION: NEXT_Q,
},
},
finish: {
type: "final",
},
},
},
{
guards: {
newTotalQuestionsIsValidValue: (
context,
event
) => {
if (event.type !== "START") return false;
// return context.totalQuestions > 0;
return true
},
canGoToNextQuestion: (context) => {
return context.currentQuestion < context.totalQuestions;
},
isCorrect: (ctx) => {
return ctx.answer?.selectedOption === ctx.answer?.value;
},
isIncorrect: (ctx) => {
return ctx.answer?.selectedOption !== ctx.answer?.value;
},
},
actions: {
clearErrorMessage: assign((context) => {
return { errorMessage: undefined };
}),
goToNextQuestion: assign((context) => {
return { currentQuestion: context.currentQuestion + 1 };
}),
assignTotalQuestionsToContext: assign(
(context, event) => {
// questions array starts from index 0
// reduce one from the total length of questions array
if (event.type !== "START") return {};
return {
totalQuestions: 10,
};
}
),
},
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment