Counter State Machine
A counter state machine that maintains a numeric value that can be incremented, decremented, and reset, demonstrating how to store and update data within a state.
Implementation Details
Section titled “Implementation Details”This example demonstrates:
- Storing data within state using the
data
property - Self-transitions that preserve the state key but change the data
- Using the
addEventApi
helper to create a clean API for transitions
Unlike the toggle example which had two distinct states, this counter example uses a single state (Active
) with different data values, showing how state machines can maintain internal data.
Machine Code
Section titled “Machine Code”import { createMachine, defineStates, onLifecycle, addEventApi,} from "matchina";
export const createCounterMachine = () => { const states = defineStates({ Active: (count: number = 0) => ({ count }), });
// Create a machine with proper transitions const machine = addEventApi( createMachine( states, { Active: { increment: "Active", decrement: "Active", reset: () => () => states.Active(0), }, }, states.Active() ) ); onLifecycle(machine, { Active: { on: { increment: { effect: (ev) => { ev.to.data.count = ev.from.data.count + 1; }, }, decrement: { effect: (ev) => { ev.to.data.count = ev.from.data.count - 1; }, }, }, }, }); return machine;};export type CounterMachine = ReturnType<typeof createCounterMachine>;
import { type CounterMachine } from "./machine";
// A counter view component that uses the enhanced CounterMachineexport const CounterView = ({ machine }: { machine: CounterMachine }) => { // Get current state const currentState = machine.getState(); return ( <div className="flex flex-col items-center"> <div className="text-6xl font-bold mb-4">{currentState.data.count}</div> <div className="flex space-x-2"> <button className="px-4 py-2 rounded bg-blue-500 text-white hover:bg-blue-600" onClick={() => machine.api.increment()} > + </button> <button className="px-4 py-2 rounded bg-blue-500 text-white hover:bg-blue-600" onClick={() => machine.api.decrement()} > - </button> <button className="px-4 py-2 rounded bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 hover:bg-gray-300 dark:hover:bg-gray-600" onClick={() => machine.api.reset()} > Reset </button> </div> </div> );};
import { useMemo } from "react";import { CounterView } from "./CounterView";import { createCounterMachine } from "./machine";
export function CounterDemo() { const machine = useMemo(createCounterMachine, []); return <CounterView machine={machine} />;}
Additional Notes
Section titled “Additional Notes”- The counter uses transition functions that return a new state object with updated data
- All transitions are self-transitions (staying in the
Active
state) - This pattern is common for state machines that need to track values but don’t have distinct behavioral states