Register and call typed hooks
Registering and calling hooks in hookable allows you to build extensible systems where multiple handlers can respond to the same event in a predictable sequence. By using createHooks, you can define a strict contract for your hooks, providing type guidance for handler signatures and arguments.
Defining a Hook Contract
To ensure type safety, define an interface or type alias where keys represent hook names and values represent the callback signatures. These callbacks should return void or a Promise<void>. You then pass this type to createHooks to get a typed Hookable instance.
The hook method registers a named handler. When multiple handlers are registered for the same hook, they are executed in the order they were added. The callHook method triggers this execution and returns a promise that resolves once all handlers have finished. If any handler throws an error or returns a rejecting promise, the callHook promise will reject with that error, and subsequent handlers in the sequence will not be executed.
import { createHooks } from "hookable";
interface MyHooks {
"start": () => void | Promise<void>;
}
async function runExample() {
const hooks = createHooks<MyHooks>();
const trace: string[] = [];
const handlerOne = () => {
trace.push("handler1");
};
const handlerTwo = () => {
trace.push("handler2");
};
hooks.hook("start", handlerOne);
hooks.hook("start", handlerTwo);
await hooks.callHook("start");
console.assert(trace[0] === "handler1");
console.assert(trace[1] === "handler2");
}
await runExample();
Managing Handler Lifecycles
The hook method returns an unregister function. Invoking this function removes the specific handler from the hookable instance, ensuring it is no longer called during subsequent callHook executions. This is useful for temporary listeners or cleaning up resources to prevent unexpected side effects.
import { createHooks } from "hookable";
interface AppHooks {
"app:shutdown": () => void | Promise<void>;
}
async function runUnregisterExample() {
const hooks = createHooks<AppHooks>();
const trace: string[] = [];
const temporaryHandler = () => {
trace.push("temporary handler was called");
};
const unregister = hooks.hook("app:shutdown", temporaryHandler);
await hooks.callHook("app:shutdown");
console.assert(trace.length === 1);
unregister();
await hooks.callHook("app:shutdown");
console.assert(trace.length === 1);
}
await runUnregisterExample();