Stopwatch React with data and transition parameters
Stopped
0s
Loading...
{ "type": "__initialize", "to": { "data": { "elapsed": 0 }, "key": "Stopped" } }
This is a function-oriented example. It is the opposite of declarative.
It also uses state data
for tracking elapsed
time and does not rely on React useMemo
or useState
for state or effects.
It is a bit more verbose, but it shows the power of using functions for state data and transition handlers.
If you’re into that sort of thing. I am.
But admittedly, it’s not as pretty, and a little harder to follow.
This example shows how we can use functions for a richer typescript experience.
Full Code
52 lines,
1589 chars
import React, { useMemo } from "react";
import { effect, enter, setup, when } from "matchina";
import { useMachine } from "matchina/integrations/react";
import { StopwatchDevView, tickEffect } from "./StopwatchCommon";
import { matchina } from "matchina/dev/matchina";
function useStopwatch() {
// Define the state machine
const stopwatch = useMemo(() => {
const model = Object.assign(matchina(
//state data creators
{
Stopped: () => ({ elapsed: 0 }),
Ticking: (elapsed = 0) => ({ elapsed, at: Date.now() }),
Suspended: (elapsed = 0) => ({ elapsed }),
},
// transitions
({ Stopped, Ticking, Suspended }) => ({
Stopped: { start: Ticking },
Ticking: {
_tick: () => (ev) => Ticking(!ev ? 0 : ev?.from.data.elapsed + (Date.now() - ev?.from.data.at)),
stop: Stopped,
suspend: () => (ev) => Suspended(ev?.from.data.elapsed),
clear: Ticking,
},
Suspended: {
stop: Stopped,
resume: () => (ev) => Ticking(ev?.from.data.elapsed),
clear: Suspended,
},
}),
// initial state
({Stopped}) => Stopped(),
), {
elapsed: 0
});
setup(model.machine)(
enter(when(ev => ev.to.is("Ticking"), () => tickEffect(model._tick))),
effect(ev => { stopwatch.elapsed = ev.to.data.elapsed ?? 0 })
)
return model;
}, []);
useMachine(stopwatch.machine);
return stopwatch;
}
export function Stopwatch () {
const stopwatch = useStopwatch()
return <StopwatchDevView stopwatch={stopwatch} />
}