💻 Tech
In cases where you want to manually clean up resources with effect(), you should make a reference to it then call destroy()
effectRef: EffectRef | null = null;
counterSignal = signal(0);
constructor() {
this.effectRef = effect(() => {
console.log(`counter value: ${this.counterSignal()}`);
});
increment() {
this.counterSignal.update((x) => x + 1);
}
destroy() {
this.effectRef?.destroy(); // destroy effect()
}
But how about if you want to destroy the resource being managed by effect()? In the example below, on page load, it shows 0 counter value. But when you call increment() (e.g., from a button click), no log is being made. This is because counterSignal is not anymore detected as a dependency in effect() because it is scoped already by setTimeout. Note that for the effect() to run, the dependencies that have changes should be immediately available to it.
constructor() {
this.effectRef = effect(() => {
const timeout = setTimeout(() => {
console.log(`counter value: ${this.counterSignal()}`);
}, 1000);
});
}
increment() {
this.counterSignal.update((x) => x + 1);
}
Now, in this code, we set counter inside the scope of effect, so the log here now runs because counterSignal is a dependency of the effect and a counter variable ensures the log statement has access to the value. Now, the question is, since this is about clean up, what do we do with leftover timeout, how do we ensure it’s cleaned up?.
constructor() {
const counter = this.counterSignal();
this.effectRef = effect(() => {
const timeout = setTimeout(() => {
console.log(`counter value: ${counter}`);
}, 1000);
});
}
increment() {
this.counterSignal.update((x) => x + 1);
}
With the final code below, the callback clean up is now being called when the effect() reference is destroyed. In addition, before the next effect() runs, the callback clean up is also run. This code just simplifies this dependency and the need to manually clean up resources. Of course, we don’t use timeout this way; it just easy to demonstrate how Angular performs the clean up with this example.
constructor() {
const counter = this.counterSignal();
this.effectRef = effect(() => {
const timeout = setTimeout(() => {
console.log(`counter value: ${counter}`);
}, 1000);
onCleanup(() => {
console.log('calling clean up')
clearTimeout(timeout);
})
});
}
increment() {
this.counterSignal.update((x) => x + 1);
}