State-driven interfaces with XState
Hey FriEnds đź‘‹
Diky Arga
FE Dev - PandaCare

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?

Basically, we as a frontend dev is getting paid to manage states
isLoading?
isError?
isAbela?
How do we manage the state?

basic fetch and set data

loading too long, add loading indicator

sometime we don't get what we want, display error

sometime we have moody user, who suddenly want to cancel the request

What could go wrong?
- What if fetch fails?
- What if the user taps the button twice?
- What if the user wants to cancel the fetch request?
- Disable the button when fetching
- Show loading progress
- Show error message
- What if the 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 - Forgot to add guards
- Not declarative
what to do with isSuccess? - No separation of concerns
Spaghetti 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 states)
- Easier to test
- Separation of concerns (view, logic, data fetch)
- Framework Agnostic
- Visualization
- A 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
State-driven interfaces with XState
By Diky Arga
State-driven interfaces with XState
- 280