0 day streak
javascript / intermediate
Snippet

Managing Multi-State UI Transitions with Discriminated State Machine Objects

Instead of juggling multiple disjointed boolean flags (like isLoading, isError, isSuccess), using a single discriminated state object with frozen enum-like string constants guarantees valid state transitions and simplifies conditional control flow via switch statements.

snippet.js
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import { useState } from 'react';
const UI_STATUS = Object.freeze({
IDLE: 'idle',
SUBMITTING: 'submitting',
SUCCESS: 'success',
FAILURE: 'failure'
});
export function AsyncActionView() {
const [state, setState] = useState({ status: UI_STATUS.IDLE, error: null });
const executeAction = async () => {
setState({ status: UI_STATUS.SUBMITTING, error: null });
try {
await new Promise((res) => setTimeout(res, 1000));
setState({ status: UI_STATUS.SUCCESS, error: null });
} catch (err) {
setState({ status: UI_STATUS.FAILURE, error: err.message });
}
};
switch (state.status) {
case UI_STATUS.SUBMITTING:
return <button disabled>Processing...</button>;
case UI_STATUS.SUCCESS:
return <p className="success-msg">Operation completed successfully!</p>;
case UI_STATUS.FAILURE:
return <div className="err-banner">Error: {state.error}</div>;
case UI_STATUS.IDLE:
default:
return <button onClick={executeAction}>Start Action</button>;
}
}
react
Breakdown
1
const UI_STATUS = Object.freeze({
Creates an immutable dictionary of constant status identifiers to avoid typos and invalid states.
2
const [state, setState] = useState({ status: UI_STATUS.IDLE, error: null });
Consolidates related UI status and payload data into a single predictable state container.
3
switch (state.status) {
Evaluates the active discriminator to render the exact matching UI branch cleanly.
4
case UI_STATUS.SUBMITTING:
Handles intermediate async state by presenting a disabled loading indicator.