Slide 1

Slide 1 text

TAMING UI COMPLEXITY WITH TYPED STATES MACHINES

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

‣ Software developer @ ‣ Doing React with Typescript @work ‣ Played with Elm @home ABOUT ME BOGDAN ZAHARIA @zaboco

Slide 4

Slide 4 text

http://bit.ly/taming-ui

Slide 5

Slide 5 text

POTENTIALLY ASKED QUESTIONS ‣ Do I need to know about state machines? ‣ NO ‣ Do I need to know Typescript? ‣ A LITTLE PAQ

Slide 6

Slide 6 text

in the real world State machines

Slide 7

Slide 7 text

STATES ‣ Idle 1. INSERT_CAN ‣ Loaded 2. PRESS_BUTTON ‣ Recycling 2.5 WAIT_FOR_RECYCLING ‣ Done 3. COLLECT_REWARD ‣ Idle (again)

Slide 8

Slide 8 text

Idle Loaded INSERT_CAN Recycling PRESS_BUTTON Done COLLECT_REWARD IDEAL WAIT_FOR_RECYCLING

Slide 9

Slide 9 text

Idle Loaded INSERT_CAN Recycling PRESS_BUTTON Done WAIT_FOR_RECYCLING COLLECT_REWARD REAL PRESS_BUTTON COLLECT_REWARD " WAIT_FOR_RECYCLING PRESS_BUTTON PRESS_BUTTON INSERT_CAN INSERT_CAN INSERT_CAN

Slide 10

Slide 10 text

But in software we can do better

Slide 11

Slide 11 text

WHAT ABOUT XSTATE? ‣ Great library, a lot of features. ‣ Can specify states and transitions: const recyclingMachine = Machine({ initial: "Idle", states: { Idle: { on: { INSERT_CAN: "Loaded" } }, "// ""... } }); ‣ Written in Typescript ‣ Does not enforce transitions recyclingMachine.transition('INSERT_CAN') "// Loaded recyclingMachine.transition('INSERT_CAN') "// still Loaded

Slide 12

Slide 12 text

WHAT ABOUT REDUX? What about it? Redux is not a state machine Or is it? Maybe Redux is a machine with 1 state.

Slide 13

Slide 13 text

But… Redux already uses “state” We’ll just call it Model

Slide 14

Slide 14 text

Let’s see an example

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

function reducer( model: AppModel = {}, action: AppAction ): AppModel { switch (action.type) { case 'LOGIN_SUCCESS': return { name: action.payload }; case 'LOGIN_ERROR': return { error: action.payload }; case 'LOGOUT': return {}; case 'NAME_TO_LOWERCASE': return { name: model.name.toLowerCase() } } } OUR REDUX APP type AppModel = { error"?: string; name"?: string; }; type AppAction = | { type: 'LOGIN_SUCCESS'; payload: string; } | { type: 'LOGIN_ERROR'; payload: string; } | { type: 'NAME_TO_LOWERCASE' } | { type: 'LOGOUT' }; ❗

Slide 17

Slide 17 text

Maybe we need to split the Model by state

Slide 18

Slide 18 text

LoggedOut { error!?: string } LoggedIn { name: string } LOGIN_SUCCESS (string) LOGIN_ERROR (string) LOGOUT NAME_TO_LOWERCASE

Slide 19

Slide 19 text

But how do we restrict transitions?

Slide 20

Slide 20 text

View HOW UI IS HANDLED IN REDUX Store uses the model dispatch Machine transition OR XSTATE

Slide 21

Slide 21 text

LoggedIn View WHAT IF WE INVERT THE CONTROL? Machine {name:string} LOGOUT or NAME_TO_LOWERCASE LoggedOut View {error!?:string} LOGIN_SUCCESS or LOGIN_ERROR

Slide 22

Slide 22 text

Introducing typed-machine

Slide 23

Slide 23 text

Demo Time ▶

Slide 24

Slide 24 text

NOT ONLY REACT ‣ A machine is abstract ‣ Needs adapters (e.g. MachineAdapter for React) ‣ Same machine used with different views/adapters ‣ Other implemented (more or less) adapters: ✓ Vue.js ✓ CLI ✓ Koa (server-side)

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

Thanks! @zaboco

Slide 27

Slide 27 text

NEXT STEPS ๏ Update docs ๏ Scaling - machines inside machines (as model) [WIP] ๏ Async transitions (e.g. fetch requests) [WIP] x Routing x History - undo/redo x Time travel debugging x Eslint plugin (multiple dispatches, unreachable states)

Slide 28

Slide 28 text

WHAT IF I DON’T WANT TO USE TYPESCRIPT ‣ TS is still JS, so typed-machine can be used with JS - (all the type guarantees are lost) ‣ Try XState or others ‣ Port typed-machine to Flow or Reason

Slide 29

Slide 29 text

๏ Repo: https://github.com/zaboco/typed-machine/tree/ jsheroes-demo ๏ Related talk: “Making Impossible states impossible” ‣ https://youtu.be/IcgmSRJHu_8 LINKS