Union Machines
Compose a machine out of matchboxes:
52 lines,
1040 chars
const states = matchboxFactory(
{
Idle: () => ({}),
Done: (x: number) => ({ result: x }),
},
"key",
);
type State = MemberOf<typeof states>;
const events = matchboxFactory(
{
execute: (x: number) => x,
},
"type",
);
type Event = MemberOf<typeof events>;
const transition = (state: State, event: Event) =>
state.match({
Idle: () =>
event.match({
execute: (x) => states.Done(x),
}),
Done: () => state,
});
function createMachine(initialState: State) {
let currentState = initialState;
return {
getState: () => currentState,
send(event: Event) {
currentState = transition(currentState, event);
},
};
}
// Usage
const machine = createMachine(states.Idle());
machine.send(events.execute(123));
console.log(machine.getState().key);
const state = machine.getState();
if (state.is("Done")) {
state.data.result = 123;
}
// OR
const result = state.as("Done").data.result;
Use matchboxes for state:
39 lines,
1064 chars
const states = defineStates({
NOT_LOADED: () => ({}),
LOADING: () => ({}),
LOADED: (data: { whatever: true }) => ({ data }),
ERROR: (error: Error) => ({ error }),
});
type DataState = FactoryState<typeof states>;
const DataComponent: React.FC = () => {
const [state, setState] = useState<DataState>(states.NOT_LOADED());
return (
<div>
{state.match({
NOT_LOADED: () =>
(
<button
onClick={() => {
fetch("/data")
.then((response) => response.json())
.then((data) => setState(states.LOADED(data)))
.catch((error) => setState(states.ERROR(error)));
}}
>
Load Data
</button>
) as any,
LOADING: () => "Loading...",
LOADED: ({ data }) => JSON.stringify(data),
ERROR: ({ error }) => `ops, ${error.message}`,
})}
</div>
);
};