Skip to content

Lifecycle & Hooks

Lifecycle hooks allow you to intercept and react to state transitions in your state machines. They provide a powerful way to:

  1. Guard transitions to prevent invalid state changes
  2. React to state changes with side effects
  3. Modify state during transitions
  4. Log state changes for debugging

Matchina provides several types of hooks:

Guard hooks can prevent transitions from occurring:

import { matchina, setup, guard } from "matchina";
const counter = matchina(
{
Idle: () => ({ count: 0 }),
Counting: (count: number) => ({ count }),
},
{
Idle: {
start: "Counting",
},
Counting: {
increment: (state) => ({
...state,
data: { count: state.data.count + 1 },
}),
reset: "Idle",
},
},
"Idle"
);
// Add a guard to prevent counting above 10
setup(counter)(
guard((ev) => {
if (ev.type !== "increment") return true;
if (!ev.from.is("Counting")) return true;
return ev.from.data.count < 10; // Only allow if count < 10
})
);

Enter hooks run when entering a specific state:

setup(counter)(
// Enter hook - log when entering specific states
enter((ev) => {
console.log(`Entering state: ${ev.to.key}`);
if (ev.to.is("Counting") && ev.to.data.count === 10) {
console.log("Maximum count reached!");
}
})
);

Leave hooks run when leaving a specific state:

setup(counter)(
// Leave hook - log when leaving states
leave((ev) => {
console.log(`Leaving state: ${ev.from.key}`);
if (ev.from.is("Counting") && ev.to.is("Idle")) {
console.log("Counter reset!");
}
})
);

Effect hooks run after state transitions and can be used for side effects:

setup(counter)(
// Effect hook - run side effects after transitions
effect((ev) => {
if (ev.to.is("Counting") && ev.to.data.count % 5 === 0) {
console.log(`Milestone: ${ev.to.data.count}`);
}
})
);

You can combine multiple hooks using the setup function:

setup(counter)(
guard((ev) => {
/* guard logic */
}),
enter((ev) => {
/* enter logic */
}),
leave((ev) => {
/* leave logic */
}),
effect((ev) => {
/* effect logic */
})
);

Each hook receives an event object with context about the transition:

type LifecycleEvent = {
type: string; // The transition type (e.g., "increment")
from: State; // The state before transition
to: State; // The state after transition
params: any[]; // Parameters passed to the transition
};

Now that you understand lifecycle hooks, explore these related guides: