Counter with Reset
Problem
You need a counter that tracks a numeric value and can be incremented, decremented, or reset to zero. This is the simplest Machine pattern for context mutation via actions.
Solution
Use a single idle state with self-transitions that apply actions to mutate context. Self-transitions re-enter the same state, fire actions, and update context atomically.
ts
import { machine } from '@vielzeug/clockwork';
type Event = { type: 'DEC' } | { type: 'INC' } | { type: 'RESET' };
const m = machine({
context: { count: 0 },
initial: 'idle',
states: {
idle: {
on: {
DEC: {
actions: [
({ context }) => {
context.count -= 1;
},
],
target: 'idle',
},
INC: {
actions: [
({ context }) => {
context.count += 1;
},
],
target: 'idle',
},
RESET: {
actions: [
({ context }) => {
context.count = 0;
},
],
target: 'idle',
},
},
},
},
});
console.log(m.send({ type: 'INC' })); // 'transitioned'
console.log(m.send({ type: 'INC' })); // 'transitioned'
console.log(m.context.value.count); // 2
m.send({ type: 'RESET' });
console.log(m.context.value.count); // 0Pitfalls
- Actions receive a cloned draft. Mutate
contextdirectly inside action functions — this is the intended pattern. - Self-transitions run exit/entry actions. If you add
entryorexittoidle, they fire on every self-transition, not just on first entry. Use transitionactionsfor per-event mutations. - For nested objects, mutate in place or spread explicitly.
context.nested.key = valueworks. If replacing the whole nested object, assign it:context.nested = { ...context.nested, key: value }.
Related
- Data Fetching with Error Recovery — Using actions inside async invoke results
- Auth Flow with Guards — Combining actions with guards
- API Reference —
ActionFn