The art of explicit
User interfaces

Farzad Yousefzadeh

@FARZAD_YZ

@FARZAD_YZ

✈️   ENGINEER

💻   ENGINNER

Living in

🇫🇮

@

User interface is

Challenging

Command line

Direct manipulation ui

Why User Interface is hard?

Run on different platforms

Platforms are different

Partial standards

Styling challenges

SOurces of Data

Unpredictable consumers

Accessibility

internationalization

State management

Renderer         = View

(State)

Renderer         = View

(State)

Behavior

Explicitness

Predictable

Explicit state

Renderer         = View

(State)

Explicit UI

Own mistakes in state management

IMplicit states

before fetching

after fetching

isFetching: true
type FetchingState =
  | BeforeFetching
  | Fetching
  | FetchedValidData
  | FetchedCorruptedData
  | FetchedError

Missing states

Error

Empty

Conflicting states

type LayoutProps = {
  main?: boolean;
  header?: boolean;
  right?: boolean;
}
type LayoutProps = {
  main?: boolean;
  header?: boolean;
  right?: boolean;
}
<Layout main />
<Layout header />
<Layout right />

8

3

type LayoutType =
    | Main
    | Header
    | Right
<MainLayout />
<HeaderLayout />
<RightLayout />

optional

Boolean

type LayoutProps = {
  main?: boolean;
  header?: boolean;
  right?: boolean;
}
const layoutProps = {
  isMain: false,
  isHeader: false,
  isRight: false
}

Uncertainty

Limited values

Complexity growth

Priority will matter

isLoading: boolean
2 ^ N
isMain: true;
isHeader: true;

derived states

const ArticleList = ({list}) => {
  return (
    {list.length === 0 ? <p>Empty list</p> : null}
  )
}

const Article = ({ article }) => (
  <div>
    {article.pictures.length ? (
      <>
        <Picture src={article.pictures[0].url} />
        <PictureCaption>{article.pictures[0].caption}</PictureCaption>
      </>
    ) : null}
  </div>
);
switch(event.type) {
  case "SET_CHECKING":
    return {
      ...state,
      checking: true,
      error: undefined
    }
  case "CHECK_SUCCESS":
    return {
      ...state,
      checking: false,
      status: event.payload
    }
  case "CHECK_FAILURE":
    return {
      ...state,
      checking: false,
      error: event.payload
    }
  default:
    return state;
}
{
  status: undefined,
  checking: false,
  error: undefined
}

Problematic
conventions

type HealthCheckState
  =
  | Idle
  | Checking
  | Success status
  | Failure error
{
  status: undefined,
  checking: false,
  error: undefined
}

Grouping relevant data

Event, Action

document.getElementById("button")
  .addEventListener("click", e => {
  // Run side effects
})
dispatch({ type: "authorize_session" });

function authorizeHandler(state) {
  if (state.session.isLoggedIn) {
    dispatch({ type: "session_authorized" });
  } else {
    if (isSessionValidForRefreshing(state.session.lastLoggedIn))
    {
      dispatch({ type: "retry_session" });
    } else {
      if (state.session !== undefined) {
        dispatch({ type: "session_logout" });
      }
    }
  }
}

Event handlers

renderers

login()

logout()

checkout()

addToCart()

LoginView

LogoutView

CheckoutView

CartView

Separately modeled

FSM
& Statecharts

FSM(State + Event) => NextState + SideEffects

Purity vs Idempotence

Reducing logic

Finite state machines

Machine({
  id: "session",
  initial: "logged_out",
  states: {
    logged_out: {
      on: {
        LOGIN: "authorizing"
      },
      id: "session_error",
      initial: "empty",
      states: {
        empty: {},
        present: {
          onEntry: "showError",
          on: {
            RETRY: "authorizing"
          }
        }
      }
    },
    logged_in: {
      on: {
        LOGOUT: "authorizing"
      }
    },
    authorizing: {
      on: {
        SUCCESS: "logged_in",
        ERROR: "logged_out"
      }
    }
  }
})

Check geolocation support

Detect zone based on geolocation

watch geolocation changes only after the firt zone is detected

type State =
  | { type: "location_pending" }
  | { type: "location_supported.zone_idle" }
  | { type: "location_supported.zone_pending" }
  | { type: "location_supported.zone_error"; error: string }
  | { type: "location_supported.zone_available.not_watching"; zone: Zone }
  | {
      type: "location_supported.zone_available.watching.listening";
      zone: Zone;
      watchId: number;
    }
  | {
      type: "location_supported.zone_available.watching.updating";
      zone: Zone;
      watchId: number;
    };

Visual behavior

UI as a whole

watch geolocation changes only after the firt zone is detected

MAKE IMPOSSIBLE STATES IMPOSSIBLE

Making Impossible States Impossible by Richard Feldman

MAKE IRRELEVANT DATA UNAVAILABLE

DO NOT TEST SOMETHING THAT IS NOT SUPPOSED TO HAPPEN

TESTING IS GOOD.

IMPOSSIBLE IS JUST BETTER.

Making Impossible States Impossible by Richard Feldman

THANK YOU!

Farzad Yousefzadeh

@FARZAD_YZ