Skip to content

Instantly share code, notes, and snippets.

@ericclemmons ericclemmons/machine.js
Last active Mar 13, 2020

Embed
What would you like to do?
Generated by XState Viz: https://xstate.js.org/viz
// This state machine is trying to figure out how to allow a user to call `await Auth.getUser()` multiple times throughout their app and get the current user once it resolves.
// Utility to resolve a Promise externally
const defer = () => {
let res,rej;
const promise = new Promise((resolve, reject) => {
res = resolve;
rej = reject;
})
promise.resolve = res;
promise.reject = rej
return promise
}
// External-facing service layer
const Auth = {
// Internal reference that we can replace after LOG_OUT
_currentUser: defer(),
// How users would request the current user (without firing new API calls)
async getUser() {
return Auth._currentUser;
}
}
const logUser = () => {
// User's code to fetch user in their app...
Auth.getUser().then(user => {
if (user) {
console.log("We're in!", user);
} else {
console.warn("Not authenticated :(");
}
setTimeout(logUser, 1000);
})
}
logUser();
console.log("Creating authMachine...");
const authMachine = Machine({
id: 'auth',
initial: 'checkSession',
context: {
error: null,
user: null,
},
states: {
'checkSession': {
invoke: {
src: "fetchUser",
onDone: {
target: "authenticated",
actions: assign({
user: (context, event) => event.data
})
},
onError: "signIn"
},
exit: "resolveCurrentUser"
},
'authenticated': {
on: {
LOG_OUT: {
target: 'signIn',
actions: "clearCurrentUser"
}
}
},
'signIn': {
initial: 'idle',
states: {
'idle': {
on: {
NEXT: 'authenticate',
SIGN_UP: '#auth.signUp',
FORGOT_PASSWORD: '#auth.forgotPassword',
}
},
'history': { type: 'history' },
'authenticate': {
invoke: {
src: 'authenticate',
onDone: {
target: '#auth.authenticated',
actions: assign({
user: (context, event) => event.data
})
},
onError: {
target: 'history',
}
}
}
},
},
'signUp': {
PREV: 'signIn',
},
'forgotPassword': {
PREV: 'signIn',
},
}
}, {
actions: {
clearCurrentUser: (context, event) => {
Auth._currentUser = null
},
resolveCurrentUser: (context, event) => {
if (Auth._currentUser.then) {
Auth._currentUser.resolve(event.data);
} else {
Auth._currentUser = event.data
}
}
},
services: {
authenticate: (context, event) => {
return new Promise(resolve => {
setTimeout(() => {
resolve({ name: "Logged In User"})
}, 1000)
})
},
fetchUser: (context, event) => {
return new Promise(resolve => {
setTimeout(() => {
resolve({ name: "Existing Session User" });
}, 1000)
})
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.