loading...

State-driven development of user interface components

ifelseapps profile image Maxim Pavlov ・2 min read

I often see code describing the state of the user interface that needs to be simplified.

Let's see code that outputs a list of users.

  <ng-container *ngIf="isLoading && !error">Loading...</ng-container>
  <ul *ngIf="users && users.length && !error">
    <li *ngFor="let user of users">{{user.name}}</li>
  </ul>
  <ng-container *ngIf="!error && !loading && users && !users.length">Nothing found</ng-container>
  <ng-container *ngIf="!isLoading && error">{{error.message}}</ng-container>

This code is just awful. It is difficult to read and maintain.
I prefer another way. I used to read about the theory of finite-state machines. The state machine has a finite set of states, and it is in one of these states at each moment.

We have four states of the user's list:

  1. Loading
  2. Users loaded
  3. Users were loaded with errors
  4. Users were not founded

Let's describe the state with a discriminated union.

type State =
  | { status: 'loading' }
  | { status: 'success', data: IUser[] }
  | { status: 'failed', error: Error }
  | { status: 'not-founded' }

Let's rewrite view logic.

  <ng-container *ngIf="state.status === 'loading'">Loading...</ng-container>
  <ul *ngIf="state.status === 'success'">
    <li *ngFor="let user of state.data">{{user.name}}</li>
  </ul>
  <ng-container *ngIf="state.status === 'not-found'">Nothing found</ng-container>
  <ng-container *ngIf="state.status === 'failed'">{{state.error.message}}</ng-container>

You can make the state type universal by using generics.

type State<TSuccessData> =
  | { status: 'loading' }
  | { status: 'success', data: TSuccessData }
  | { status: 'failed', error: Error }
  | { status: 'not-founded' }
type UsersListState = State<IUser[]>;

This code is more reading and self-documenting. Presently your IDE gives better hints for you.
Code completion example
Code completion example
Your team will be grateful to you.

P.S. Sorry for my English. It is my first article in English.

Posted on by:

ifelseapps profile

Maxim Pavlov

@ifelseapps

Senior frontend engineer, traveler, happy husband and dad of my little princess.

Discussion

markdown guide