The art of
explicit and consistent
user interfaces
Farzad Yousef Zadeh
@farzad_yz
✈ Aerospace engineer 🔭 Astrophysicist
Senior Software Engineer @
CurrentState
PreviousState
github.com/farskid
twitter.com/farzad_yz
🇫🇮
jQuery
AngularJS
BackboneJS
Phonegap / Cordova
React
React Native
Redux
Dojo Toolkit
Vue
aka
aka
Alan Kay made the term Windowing UI popular
source: constructing the user interface with Statecharts by Ian Horrocks
(WAY TOO MUCH)
😱 @media foldable
justify-content: flex-start
How the software is supposed to work
The form is supposed to be submitted
The input is supposed to be validated
The button is supposed to be clicked
The button is supposed to be enabled when the user hasn't searched yet
The button is supposed to be disabled when the search is happening
The button is supposed to be enabled when the search is done
A is supposed to happen while...
A is supposed to happen after...
A is supposed to happen before...
➡️ Booleans
isHappening: true
isHappening: false
isHappening: false
isFetching: false
isFetching: true
isFetching: false
isFetching: true
isFetching: false
Pending
Fulfilled
Rejected
Fulfilled with valid data
Fulfilled with corrupted data
Timed out!
Token expired
Server went down
1
2
3
4
5
6
7
8
state: idle
state: pending
state: pending
state: success | failure
state: success | failure
failureState: offline | timeout | unauthorized | ...
successState: valid | corrupted
Things that can not happen at the same time
isValidating: boolean
isSubmitting: boolean
isValidating: true, isSubmitting: true
isValidating: true, isSubmitting: false
isValidating: false, isSubmitting: true
isValidating: false, isSubmitting: false
Form is idle
Form is being submitted
Form is being validated
WTF ???
formState: | idle | validating | validated_success | validated_failure | submitting | submitted_success | submitted_failure| ...
They do not carry an inherent meaning.
They do not let the complexity grow linearly (2^N)
They do not let the complexity grow linearly (2^N)
By using Conflicting booleans (dependants), the order matters!
header
main
side
isHeader: boolean isMain: boolean isSide: boolean
type LayoutProps = { main?: boolean, header?: boolean, side?: boolean }
<Layout main />
<Layout header />
<Layout side />
3😊
2^3 = 8😱
items.length === 0
Empty State
items.length > 0
Happy State
if (authState.token == null) { authService.refreshToken(); }
if (!formState.isSubmitting) { formService.submit(); }
<Button disabled={isSubmitting} />
if (action.type === "LOGOUT") { authService.logout(...) }
states: { loggedout: { on: {LOGIN: {actions: "login"}} }, loggedin: { on: {LOGOUT: {actions: "logout"}} } }
states: { idle: {on: {SEARCH: "searching"}}, searching: {on: {SUCCESS: "render", ERROR: "error"}}, render: {on: {SEARCH: "searching"}} }
states: {
location_find: {
invoke: {
src: "getCurrentLocation",
// web
initMachine(machine).withConfig({
services: {
getCurrentLocation: navigator.geolocation...
}
})
// mobile
initMachine(machine).withConfig({
services: {
getCurrentLocation: ReactNative.geolocation...
}
})
// test environment
initMachine(machine).withConfig({
services: {
getCurrentLocation: Promise.resolve(setTimeout(...))
}
})
Thank You!
Farzad Yousef Zadeh
@farzad_yz