Created
February 11, 2020 01:05
-
-
Save zushenyan/369fe4b48260d8ad3d7bcaca1e303ab4 to your computer and use it in GitHub Desktop.
xstate
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
import { Machine, Interpreter, assign, spawn } from 'xstate' | |
import InputMachine, { | |
Context as InputContext, | |
Schema as InputSchema, | |
Events as InputEvents | |
} from './inputMachine' | |
import { submitForm, Response } from './api' | |
export type Context = { | |
email: Interpreter<InputContext, InputSchema, InputEvents> | undefined | |
password: Interpreter<InputContext, InputSchema, InputEvents> | undefined | |
error: string | |
} | |
export type Schema = { | |
states: { | |
initializing: {}, | |
idle: { | |
states: { | |
ready: {}, | |
error: { | |
states: { | |
client: {}, | |
server: {} | |
} | |
} | |
} | |
}, | |
submitting: { | |
states: { | |
clientValidation: {}, | |
serverValidation: {} | |
} | |
}, | |
submitted: {} | |
} | |
} | |
type SubmitFormEvent = { | |
type: 'error.platform.submitForm' | |
data: Response | |
} | |
export type Events = | |
{ type: 'SUBMIT' } | |
| InputEvents | |
| SubmitFormEvent | |
const spawnFields = assign<Context, Events>({ | |
email: () => spawn(InputMachine, { name: 'email' }), | |
password: () => spawn(InputMachine, { name: 'password' }), | |
}) | |
const enableFields = (ctx: Context) => { | |
ctx?.email?.send('IDLE') | |
ctx?.password?.send('IDLE') | |
} | |
const disableFields = (ctx: Context) => { | |
ctx?.email?.send('DISABLE') | |
ctx?.password?.send('DISABLE') | |
} | |
const validateFields = (ctx: Context) => { | |
ctx?.email?.send('VALIDATE') | |
ctx?.password?.send('VALIDATE') | |
} | |
const invalidFields = (ctx: Context) => { | |
return ctx?.email?.state.matches('validation.error') | |
|| ctx?.password?.state.matches('validation.error') | |
|| false | |
} | |
const setError = assign<Context, Events>({ | |
error: (ctx, event) => (event as SubmitFormEvent).data.message | |
}) | |
const submitFormService = (ctx: Context) => { | |
const email = ctx?.email?.state.context.value ?? '' | |
const password = ctx?.password?.state.context.value ?? '' | |
return submitForm({ email, password }) | |
} | |
export default Machine<Context, Schema, Events>( | |
{ | |
initial: 'initializing', | |
id: 'root', | |
context: { | |
email: undefined, | |
password: undefined, | |
error: '', | |
}, | |
states: { | |
initializing: { | |
entry: ['spawnFields'], | |
on: { | |
'': 'idle.ready' | |
} | |
}, | |
idle: { | |
entry: ['enableFields'], | |
on: { | |
SUBMIT: { | |
target: 'submitting.clientValidation', | |
actions: ['disableFields', 'validateFields'] | |
}, | |
}, | |
initial: 'ready', | |
states: { | |
ready: {}, | |
error: { | |
states: { | |
client: {}, | |
server: {} | |
} | |
}, | |
} | |
}, | |
submitting: { | |
initial: 'clientValidation', | |
states: { | |
clientValidation: { | |
after: { | |
100: [ | |
{ target: '#root.idle.error.client', cond: 'invalidFields' }, | |
{ target: 'serverValidation' } | |
], | |
} | |
}, | |
serverValidation: { | |
invoke: { | |
id: 'submitForm', | |
src: 'submitFormService', | |
onDone: '#root.submitted', | |
onError: { | |
target: '#root.idle.error.server', | |
actions: 'setError' | |
} | |
} | |
} | |
} | |
}, | |
submitted: { | |
type: 'final' | |
} | |
} | |
}, | |
{ | |
actions: { | |
spawnFields, | |
disableFields, | |
validateFields, | |
enableFields, | |
setError, | |
}, | |
guards: { | |
invalidFields, | |
}, | |
services: { | |
submitFormService | |
} | |
} | |
) |
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
import { Machine, assign, send } from 'xstate' | |
export type Context = { | |
value: string, | |
} | |
export type Schema = { | |
states: { | |
input: { | |
states: { | |
idle: {}, | |
disabled: {}, | |
focused: {}, | |
blurred: {}, | |
} | |
}, | |
validation: { | |
states: { | |
good: {}, | |
error: { | |
states: { | |
empty: {}, | |
tooLong: {}, | |
} | |
} | |
} | |
} | |
} | |
} | |
export type Events = | |
{ type: 'FOCUS' } | |
| { type: 'DISABLE' } | |
| { type: 'IDLE' } | |
| { type: 'CHANGE', value: string } | |
| { type: 'BLUR' } | |
| { type: 'VALIDATE' } | |
const onChange = assign<Context, Events>({ | |
value: (ctx, e) => { | |
if (e.type === 'CHANGE') return e.value | |
return ctx.value | |
} | |
}) | |
const empty = (ctx: Context) => ctx.value.length === 0 | |
const tooLong = (ctx: Context) => ctx.value.length > 6 | |
export default Machine<Context, Schema, Events>( | |
{ | |
context: { | |
value: '', | |
}, | |
on: { | |
VALIDATE: [ | |
{ target: 'validation.error.empty', cond: 'empty' }, | |
{ target: 'validation.error.tooLong', cond: 'tooLong' }, | |
{ target: 'validation.good' }, | |
], | |
FOCUS: 'input.focused', | |
DISABLE: 'input.disabled', | |
BLUR: 'input.blurred' | |
}, | |
type: 'parallel', | |
states: { | |
input: { | |
initial: 'idle', | |
states: { | |
idle: {}, | |
disabled: { | |
on: { | |
IDLE: 'idle' | |
} | |
}, | |
focused: { | |
on: { | |
CHANGE: { | |
target: 'focused', | |
actions: ['onChange'] | |
}, | |
}, | |
}, | |
blurred: { | |
entry: send('VALIDATE'), | |
} | |
} | |
}, | |
validation: { | |
initial: 'good', | |
states: { | |
good: {}, | |
error: { | |
states: { | |
empty: {}, | |
tooLong: {}, | |
} | |
}, | |
} | |
} | |
} | |
}, | |
{ | |
actions: { | |
onChange, | |
}, | |
guards: { | |
empty, | |
tooLong | |
} | |
} | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment