Stopwatch with React State and Effects
Stopped
elapsed0.0s
import { createMachine, defineStates, assignEventApi } from "matchina";
/** * Standalone factory used by the docs visualizer to render the state graph. * The runtime example uses `useStopwatch` which constructs the same machine * inline with React-managed `elapsed` state. */export const createStopwatchMachine = () => { const states = defineStates({ Stopped: {}, Ticking: {}, Suspended: {}, }); return assignEventApi( createMachine( states, { Stopped: { start: "Ticking" }, Ticking: { stop: "Stopped", suspend: "Suspended", clear: "Ticking" }, Suspended: { stop: "Stopped", resume: "Ticking", clear: "Suspended" }, }, "Stopped" ) );};import { defineStates, createMachine, assignEventApi } from "matchina";import { useMachine } from "matchina/react";import { useState, useMemo, useEffect } from "react";import { tickEffect } from "../lib/tick-effect";
export function useStopwatch() { const [elapsed, setElapsed] = useState(0); const effects = useMemo( () => ({ run: () => { let lastTick = Date.now(); return tickEffect(() => { const now = Date.now(); setElapsed(stopwatch.elapsed + now - lastTick); lastTick = now; }); }, clear: () => { setElapsed(0); }, }), [] );
// Define the state machine const stopwatch = useMemo(() => { // Define states using defineStates const states = defineStates({ Stopped: {}, Ticking: {}, Suspended: {}, });
// Create the base machine with states, transitions, and initial state const baseMachine = createMachine( states, { Stopped: { start: "Ticking", }, Ticking: { stop: "Stopped", suspend: "Suspended", clear: "Ticking", }, Suspended: { stop: "Stopped", resume: "Ticking", clear: "Suspended", }, }, "Stopped" );
//Use assignEventApi to enhance the machine with utility methods return Object.assign(assignEventApi(baseMachine), { elapsed: elapsed, setElapsed: setElapsed, }); }, []);
stopwatch.elapsed = elapsed; useMachine(stopwatch); useEffect(() => { // if (stopwatch.changeProperty.type === "clear") { // effects.clear(); // } return stopwatch.getState().match( { Ticking: effects.run, Stopped: () => effects.clear, }, false ); }, [stopwatch.getState()]); return stopwatch;}export { StopwatchView } from "../stopwatch-common/StopwatchView";import { MachineVisualizer } from "@components/MachineVisualizer";import { StopwatchView } from "./StopwatchView";import { useStopwatch } from "./useStopwatch";
// Main export for importing in MDX documentationexport default function StopwatchExample() { const stopwatch = useStopwatch(); return ( <MachineVisualizer machine={stopwatch} AppView={StopwatchView} showRawState={true} /> );}
export function Stopwatch() { const stopwatch = useStopwatch(); return <StopwatchView machine={stopwatch as any} />;}