Are statecharts the next
big UI paradigm?
Luca Matteis
github.com/lmatteis
@lmatteis
Hootsuite office in Rome, Italy
Designers build images of
each possible state of our UI.
When we convert these images
(or states) into code we lose the
high-level understanding of our
app.
var isLoggedIn, isInProgress, isSuccessful;
// display the form
isLoggedIn = false;
isInProgress = false;
isSuccessful = false;
// request in flight
isLoggedIn = false;
isInProgress = true;
isSuccessful = false;
// display a successful message
isLoggedIn = true;
isInProgress = false;
isSuccessful = true;
// display welcome message + links
isLoggedIn = true;
isInProgress = false;
isSuccessful = false;
var isRequestFinished, isInProgress, isSuccessful, isFailed;
if (isInProgress) {
// request in flight, render a spinner
} else if (isRequestFinished) {
if (isSuccessful) {
// request successful, render a message
} else if (isFailed) {
// render error message + try again link
} else {
// render welcome message + links
}
} else {
// waiting for input, render login form
}
As our app grows,
understanding which section is
responsible for each state
becomes increasingly difficult.
Not all questions can be
answered using a set of
images.
• What if the user clicks the submit button repeatedly? 😛
• What if the user wants to cancel the submit while it's in-flight? ✈
• What if the user mischievously enables the disabled button? 😕
• Is there any indication that the request is fetching? 🙄
• What happens if there's an error? Can the user resubmit the form? ❓
• What if the user submits and then clicks a different page? What should
happen? 🤷
It seems we don’t have a
technological problem. We
have a thinking problem.
How do we set ourselves to
answer these questions, and be
able to have both our design
and our code depend on the
answers?
Statecharts
Awake
Day simulator
Dressed
Fed
Ready
Work
Cafe
Home
Asleep
Alarm
Dress
Eat
Eat
Dress
GoToWork
Think
Yawn
Coffee
GoHome
Read WatchTv
Empty form
Submitting
SUBMIT
RESOLVE
REJECT
Welcome
page
Error page
REGISTER
REGISTER
LOGOUT
Login page
REGISTER
Statecharts have 3
important concepts:
1) Hierarchical (nested) states 🌳
2) History states (
3) Parallel states ⏰
Empty form
Submitting
SUBMIT
RESOLVE
REJECT
Welcome
page
Error page
REGISTER
REGISTER
LOGOUT
Login page
REGISTER
Empty form
Submitting
SUBMIT
RESOLVEREJECT
Welcome
page
Error page
REGISTER
LOGOUT
Login page
Hierarchical (nested) states 🌳
Unclustering Welcome page
Login page
GetTickets
TicketsRESOLVE
REFRESH
Empty form
Submitting
SUBMIT
History states
Welcome page
GetTickets
TicketsRESOLVE
REFRESH
H
Parallel states
Welcome page
GetTickets
TicketsRESOLVE
REFRESH
Sidebar
Init
Loading
SEARCH
RESOLVE
Items
These representations can
drive both our design and
our code.
• What if the user clicks the submit button repeatedly? 😛
• What if the user wants to cancel the submit while it's in-flight? ✈
• What if the user mischievously enables the disabled button? 😕
• Is there any indication that the request is fetching? 🙄
• What happens if there's an error? Can the user resubmit the form? ❓
• What if the user submits and then clicks a different page? What should happen? 🤷
Empty form
Submitting
SUBMIT
• What if the user clicks the submit button repeatedly? 😛
• What if the user wants to cancel the submit while it's in-flight? ✈
• What if the user mischievously enables the disabled button? 😕
• Is there any indication that the request is fetching? 🙄
• What happens if there's an error? Can the user resubmit the form? ❓
• What if the user submits and then clicks a different page? What should happen? 🤷
Empty form
Submitting
SUBMIT
SUBMIT
• What if the user clicks the submit button repeatedly? 😛
• What if the user wants to cancel the submit while it's in-flight? ✈
• What if the user mischievously enables the disabled button? 😕
• Is there any indication that the request is fetching? 🙄
• What happens if there's an error? Can the user resubmit the form? ❓
• What if the user submits and then clicks a different page? What should happen? 🤷
Empty form
Submitting
SUBMIT
SUBMIT
CANCEL
• What if the user clicks the submit button repeatedly? 😛
• What if the user wants to cancel the submit while it's in-flight? ✈
• What if the user mischievously enables the disabled button? 😕
• Is there any indication that the request is fetching? 🙄
• What happens if there's an error? Can the user resubmit the form? ❓
• What if the user submits and then clicks a different page? What should happen? 🤷
Empty form
Submitting
SUBMIT
SUBMIT
CANCEL
Error
REJECT
Such diagram can be
described using JSON and
our UI code can directly be
driven by its description.
https://github.com/davidkpiano/xstate
What about setState() and
Redux?
The term state used within
the statechart formalism
simply describes a textual
label which drives our
program in understanding
what needs to happen.
To the contrary, in the
React world the term state
usually describes some
data that we use to render
our components.
Statecharts actually merry
quite well with a system
like Redux.
Redux and Statecharts
const initialState = {
isRequestFinished: false,
isInProgress: false,
isSuccessful: false,
isFailed: false
};
function reducer(state = initialState, action) {
…
}
render() {
if (isInProgress) {
// request in flight, render a spinner
} else if (isRequestFinished) {
if (isSuccessful) {
// request successful, render a message
} else if (isFailed) {
// render error message + try again link
} else {
// render welcome message + links
}
} else {
// waiting for input, render login form
}
}
Redux and Statecharts
const initialState = null;	
function reducer(state = initialState, action) {	
return machine	
.transition(state, action.type)	
.value;	
}
machine = Machine({
initial: 'init',
states: {
init: {
on: {
SUBMIT: ‘submitting’,
},
},
submitting: {
on: {
RESOLVE: ‘success’,
REJECT: ‘error’
}
},
error: {
on: {
TRY_AGAIN: ‘init’,
}
}
}
});
render() {
if (isInProgress) {
// request in flight, render a spinner
} else if (isRequestFinished) {
if (isSuccessful) {
// request successful, render a message
} else if (isFailed) {
// render error message + try again link
} else {
// render welcome message + links
}
} else {
// waiting for input, render login form
}
}
Redux and Statecharts
const initialState = null;	
function reducer(state = initialState, action) {	
return machine	
.transition(state, action.type)	
.value;	
}
machine = Machine({
initial: 'init',
states: {
init: {
on: {
SUBMIT: ‘submitting’,
},
},
submitting: {
on: {
RESOLVE: ‘success’,
REJECT: ‘error’
}
},
error: {
on: {
TRY_AGAIN: ‘init’,
}
}
}
});
render() {
const renderMap = {
[submitting]: // request in flight, render a spinner
[success]: // request successful, render a message
[error]: // render error message + try again link
[init]: // waiting for input, render login form
}
return renderMap[this.props.state];
}
Redux and Statecharts
const initialState = null;	
function reducer(state = initialState, action) {	
return machine	
.transition(state, action.type)	
.value;	
}
machine = Machine({
initial: 'init',
states: {
init: {
on: {
SUBMIT: ‘submitting’,
},
},
submitting: {
on: {
RESOLVE: ‘success’,
REJECT: ‘error’
}
},
error: {
on: {
TRY_AGAIN: ‘init’,
}
}
}
});
store.dispatch({ type: SUBMIT });
store.dispatch({ type: RESOLVE });
store.dispatch({ type: REJECT });
store.dispatch({ type: TRY_AGAIN });
You can continue using
other reducers for other
non-statechart data.
Redux and Statecharts
const initialState = null;	
function reducer(state = initialState, action) {	
return machine	
.transition(state, action.type)	
.value;	
}
store.dispatch({ type: SUBMIT });
store.dispatch({ type: RESOLVE, payload });
store.dispatch({ type: REJECT });
store.dispatch({ type: TRY_AGAIN });
function resolveReducer(state = null, action) {	
switch (action.type) {	
case RESOLVE:	
return action.payload;	
default:	
return state;	
}	
}
Redux and Statecharts
const initialState = null;	
function reducer(state = initialState, action) {	
return machine	
.transition(state, action.type)	
.value;	
}
store.dispatch({ type: SUBMIT });
store.dispatch({ type: RESOLVE, payload });
store.dispatch({ type: REJECT });
store.dispatch({ type: TRY_AGAIN });
function resolveReducer(state = null, action) {	
switch (action.type) {	
case RESOLVE:	
return action.payload;	
default:	
return state;	
}	
}
render() {
const renderMap = {
[submitting]: // request in flight, render a spinner
[success]: renderMessage(this.props.resolveData)
[error]: // render error message + try again link
[init]: // waiting for input, render login form
}
return renderMap[this.props.state];
}
Statecharts provide us with
a visual formalism that can
tie the functioning of our
code and our designs
together.
Whenever you find yourself
writing lots of isSomething
variables in your state,
maybe it’s time to give
statecharts a try!
Further reading:
Pure UI Control by Adam Solove https://medium.com/@asolove/
pure-ui-control-ac8d1be97a8d
STATECHARTS: A VISUAL FORMALISM FOR COMPLEX SYSTEMS
http://www.inf.ed.ac.uk/teaching/courses/seoc/2005_2006/
resources/statecharts.pdf
You are managing state? Think twice. by Krasimir Tsonev
http://krasimirtsonev.com/blog/article/managing-state-in-
javascript-with-state-machines-stent
Infinitely Better UIs with Finite Automata by David Khourshid
https://www.youtube.com/watch?v=VU1NKX6Qkxc
Rambling thoughts on React and Finite State Machines by
Ryan Florence https://www.youtube.com/watch?v=MkdV2-U16tc
Thanks!

Are statecharts the next big UI paradigm?

  • 1.
    Are statecharts thenext big UI paradigm?
  • 2.
  • 3.
    Designers build imagesof each possible state of our UI.
  • 5.
    When we convertthese images (or states) into code we lose the high-level understanding of our app.
  • 6.
    var isLoggedIn, isInProgress,isSuccessful; // display the form isLoggedIn = false; isInProgress = false; isSuccessful = false; // request in flight isLoggedIn = false; isInProgress = true; isSuccessful = false; // display a successful message isLoggedIn = true; isInProgress = false; isSuccessful = true; // display welcome message + links isLoggedIn = true; isInProgress = false; isSuccessful = false; var isRequestFinished, isInProgress, isSuccessful, isFailed; if (isInProgress) { // request in flight, render a spinner } else if (isRequestFinished) { if (isSuccessful) { // request successful, render a message } else if (isFailed) { // render error message + try again link } else { // render welcome message + links } } else { // waiting for input, render login form }
  • 7.
    As our appgrows, understanding which section is responsible for each state becomes increasingly difficult.
  • 8.
    Not all questionscan be answered using a set of images.
  • 9.
    • What ifthe user clicks the submit button repeatedly? 😛 • What if the user wants to cancel the submit while it's in-flight? ✈ • What if the user mischievously enables the disabled button? 😕 • Is there any indication that the request is fetching? 🙄 • What happens if there's an error? Can the user resubmit the form? ❓ • What if the user submits and then clicks a different page? What should happen? 🤷
  • 10.
    It seems wedon’t have a technological problem. We have a thinking problem.
  • 11.
    How do weset ourselves to answer these questions, and be able to have both our design and our code depend on the answers?
  • 12.
  • 13.
  • 14.
  • 15.
    Statecharts have 3 importantconcepts: 1) Hierarchical (nested) states 🌳 2) History states ( 3) Parallel states ⏰
  • 16.
  • 17.
  • 18.
    Unclustering Welcome page Loginpage GetTickets TicketsRESOLVE REFRESH Empty form Submitting SUBMIT
  • 19.
  • 20.
  • 22.
    These representations can driveboth our design and our code.
  • 23.
    • What ifthe user clicks the submit button repeatedly? 😛 • What if the user wants to cancel the submit while it's in-flight? ✈ • What if the user mischievously enables the disabled button? 😕 • Is there any indication that the request is fetching? 🙄 • What happens if there's an error? Can the user resubmit the form? ❓ • What if the user submits and then clicks a different page? What should happen? 🤷 Empty form Submitting SUBMIT
  • 24.
    • What ifthe user clicks the submit button repeatedly? 😛 • What if the user wants to cancel the submit while it's in-flight? ✈ • What if the user mischievously enables the disabled button? 😕 • Is there any indication that the request is fetching? 🙄 • What happens if there's an error? Can the user resubmit the form? ❓ • What if the user submits and then clicks a different page? What should happen? 🤷 Empty form Submitting SUBMIT SUBMIT
  • 25.
    • What ifthe user clicks the submit button repeatedly? 😛 • What if the user wants to cancel the submit while it's in-flight? ✈ • What if the user mischievously enables the disabled button? 😕 • Is there any indication that the request is fetching? 🙄 • What happens if there's an error? Can the user resubmit the form? ❓ • What if the user submits and then clicks a different page? What should happen? 🤷 Empty form Submitting SUBMIT SUBMIT CANCEL
  • 26.
    • What ifthe user clicks the submit button repeatedly? 😛 • What if the user wants to cancel the submit while it's in-flight? ✈ • What if the user mischievously enables the disabled button? 😕 • Is there any indication that the request is fetching? 🙄 • What happens if there's an error? Can the user resubmit the form? ❓ • What if the user submits and then clicks a different page? What should happen? 🤷 Empty form Submitting SUBMIT SUBMIT CANCEL Error REJECT
  • 27.
    Such diagram canbe described using JSON and our UI code can directly be driven by its description.
  • 28.
  • 29.
  • 30.
    The term stateused within the statechart formalism simply describes a textual label which drives our program in understanding what needs to happen.
  • 31.
    To the contrary,in the React world the term state usually describes some data that we use to render our components.
  • 32.
    Statecharts actually merry quitewell with a system like Redux.
  • 34.
    Redux and Statecharts constinitialState = { isRequestFinished: false, isInProgress: false, isSuccessful: false, isFailed: false }; function reducer(state = initialState, action) { … } render() { if (isInProgress) { // request in flight, render a spinner } else if (isRequestFinished) { if (isSuccessful) { // request successful, render a message } else if (isFailed) { // render error message + try again link } else { // render welcome message + links } } else { // waiting for input, render login form } }
  • 35.
    Redux and Statecharts constinitialState = null; function reducer(state = initialState, action) { return machine .transition(state, action.type) .value; } machine = Machine({ initial: 'init', states: { init: { on: { SUBMIT: ‘submitting’, }, }, submitting: { on: { RESOLVE: ‘success’, REJECT: ‘error’ } }, error: { on: { TRY_AGAIN: ‘init’, } } } }); render() { if (isInProgress) { // request in flight, render a spinner } else if (isRequestFinished) { if (isSuccessful) { // request successful, render a message } else if (isFailed) { // render error message + try again link } else { // render welcome message + links } } else { // waiting for input, render login form } }
  • 36.
    Redux and Statecharts constinitialState = null; function reducer(state = initialState, action) { return machine .transition(state, action.type) .value; } machine = Machine({ initial: 'init', states: { init: { on: { SUBMIT: ‘submitting’, }, }, submitting: { on: { RESOLVE: ‘success’, REJECT: ‘error’ } }, error: { on: { TRY_AGAIN: ‘init’, } } } }); render() { const renderMap = { [submitting]: // request in flight, render a spinner [success]: // request successful, render a message [error]: // render error message + try again link [init]: // waiting for input, render login form } return renderMap[this.props.state]; }
  • 37.
    Redux and Statecharts constinitialState = null; function reducer(state = initialState, action) { return machine .transition(state, action.type) .value; } machine = Machine({ initial: 'init', states: { init: { on: { SUBMIT: ‘submitting’, }, }, submitting: { on: { RESOLVE: ‘success’, REJECT: ‘error’ } }, error: { on: { TRY_AGAIN: ‘init’, } } } }); store.dispatch({ type: SUBMIT }); store.dispatch({ type: RESOLVE }); store.dispatch({ type: REJECT }); store.dispatch({ type: TRY_AGAIN });
  • 38.
    You can continueusing other reducers for other non-statechart data.
  • 39.
    Redux and Statecharts constinitialState = null; function reducer(state = initialState, action) { return machine .transition(state, action.type) .value; } store.dispatch({ type: SUBMIT }); store.dispatch({ type: RESOLVE, payload }); store.dispatch({ type: REJECT }); store.dispatch({ type: TRY_AGAIN }); function resolveReducer(state = null, action) { switch (action.type) { case RESOLVE: return action.payload; default: return state; } }
  • 40.
    Redux and Statecharts constinitialState = null; function reducer(state = initialState, action) { return machine .transition(state, action.type) .value; } store.dispatch({ type: SUBMIT }); store.dispatch({ type: RESOLVE, payload }); store.dispatch({ type: REJECT }); store.dispatch({ type: TRY_AGAIN }); function resolveReducer(state = null, action) { switch (action.type) { case RESOLVE: return action.payload; default: return state; } } render() { const renderMap = { [submitting]: // request in flight, render a spinner [success]: renderMessage(this.props.resolveData) [error]: // render error message + try again link [init]: // waiting for input, render login form } return renderMap[this.props.state]; }
  • 41.
    Statecharts provide uswith a visual formalism that can tie the functioning of our code and our designs together.
  • 43.
    Whenever you findyourself writing lots of isSomething variables in your state, maybe it’s time to give statecharts a try!
  • 44.
    Further reading: Pure UIControl by Adam Solove https://medium.com/@asolove/ pure-ui-control-ac8d1be97a8d STATECHARTS: A VISUAL FORMALISM FOR COMPLEX SYSTEMS http://www.inf.ed.ac.uk/teaching/courses/seoc/2005_2006/ resources/statecharts.pdf You are managing state? Think twice. by Krasimir Tsonev http://krasimirtsonev.com/blog/article/managing-state-in- javascript-with-state-machines-stent Infinitely Better UIs with Finite Automata by David Khourshid https://www.youtube.com/watch?v=VU1NKX6Qkxc Rambling thoughts on React and Finite State Machines by Ryan Florence https://www.youtube.com/watch?v=MkdV2-U16tc
  • 45.