State-driven interfaces with XState

Hey đź‘‹

Diky Arga

Squad Search EXperience 🔍

What's State?

A state is a representation of a system in a given time. State refers to the data stored in Application in the form of a string, arrays, object, etc.

ex. is user logged-in?, is Loading?, is Error? is Wahyudi?

Basically, we as a frontend dev is getting paid to manage states

isLoading?

isError?

isAbela?

How do we manage state?

basic fetch and set the data

loading lama, tambah loading bar

kadang error, tampilin error message

user plin-plan, mau batalin request

What could go wrong?

  • What if fetch fails?
  • What if user tap the button twice?
  • What if user want to cancel fetch request?
  • Disable button when fetching
  • Show loading progress
  • Show error message
  • What if request is restricted/blocked?
  • and so on..

The problems with

Event Driven Interfaces

  • The code doesn’t accurately represent our component’s intended behavior
    boolean variable like isLoading, isError, isSuccess, isMail is hard to manage and error prone
  • Not declarative
    what to do with isSuccess?
  • No separation of concerns
    nasi ruwet code in a component

A batter way (hopefully)

State Driven Interfaces

Finite State Machine

FSM

What is?

FSM is everywhere

FSM for developing UI?

XState

 JavaScript state machines and statecharts

by David K Piano

Let's simplify our life

by rewriting our owsem app with state machine

Advantages

  • Declarative (named state, listed all possible state)
  • Easier to test
  • Separation of concerns (view, logic, data fetch)
  • Framework Agnostic
  • Visualization
  • Common language between developer and designer

The cool things about XState

XState Vizualizer

yarn add @xstate/fsm

Only 1 KB

Do we really need library like XState?

No

function dogReducer(state, event) {
  switch (event.type) {
    case "FETCH":
      return {
        ...state,
        status: "loading"
      };
    case "RESOLVE":
      return {
        ...state,
        status: "success",
        dog: event.data
      };
    case "REJECT":
      return {
        ...state,
        status: "failure",
        error: event.error
      };
    case "CANCEL":
      return {
        ...state,
        status: "idle"
      };
    default:
      return state;
  }
}

const initialState = {
  status: "idle",
  dog: null,
  error: null
};

you can use reducer

No

function DogFetcher() {
  // 'idle' or 'loading' or 'success' or 'error'
  const [status, setStatus] = useState('idle');
}

you can use as simple as useState

Already miss isLoading?

function DogFetcher() {
  // 'idle' or 'loading' or 'success' or 'error'
  const [status, setStatus] = useState('idle');

  const isLoading = status === 'loading';

  return (
    // ...
    <button disabled={isLoading}>
      {/* ... */}
    </button>
    // ...
  );
}

Trade-off with State Machine

talkMachine.send('done')

<ThankYou/>

talkMachine.send('QnATime')

references

Statedriven interfaces with XState at BL

By Diky Arga

Statedriven interfaces with XState at BL

  • 493