Subscription lifecycle
A subscription is a resource. subscribe() adds a listener and returns the only cleanup function for that listener.
Module-level subscriptions
Long-lived modules can subscribe once, but they still need a shutdown path.
let stopLogging: (() => void) | null = null;
export function startLogging(): void {
if (stopLogging) return;
stopLogging = store.subscribe(() => {
console.debug("state", store.getState());
});
}
export function stopLoggingNow(): void {
stopLogging?.();
stopLogging = null;
}Component or widget subscriptions
For UI code, call unsubscribe in the component cleanup hook or widget destroy callback.
function mountWidget(node: HTMLElement): () => void {
const render = () => {
node.textContent = JSON.stringify(store.getState());
};
render();
const unsubscribe = store.subscribe(render);
return unsubscribe;
}Avoid stale owner bugs
Do not create a new subscription every render without cleaning up the old one. Do not hide unsubscribe in a place the owner cannot reach.
Listener body rules
- Read with
getState()inside the listener. - Keep work small and synchronous.
- Catch expected errors inside the listener if one bad listener should not stop later listeners.
- Avoid calling
setState()recursively unless you have a clear guard against loops.