Angular Signal effects are handy: they run whenever Signals used inside the effect change.
effect(() => {
console.log('signal variable is changed to ' + signalVariable());
})
That works when the Signal is read on the first pass, but reads inside async code or conditionals are skipped.
effect(() => {
// Runs whenever this.count changes
this.insideSimpleEffect = this.count();
});
effect(async () => {
// Does not run
await new Promise<void>((resolve) => resolve());
this.insideAwaitRequestAnimationFrame = this.count();
});
effect(() => {
// Does not run
requestAnimationFrame(() => {
this.insideRequestAnimationFrame = this.count();
});
});
effect(() => {
// Does not run
setTimeout(() => {
this.insideSetTimeout = this.count();
}, 100);
});
effect(() => {
// Does not run
if (this.insideSimpleEffect === 5) {
this.insideCondition = this.count();
}
});
You can confirm they do not run at https://stackblitz.com/edit/stackblitz-starters-unvngn?file=src%2Fmain.ts.
Cause
Effects register dependencies only when a Signal is actually read. If a read is hidden in a conditional or happens on another stack frame in async work, the effect cannot register the dependency, so it will not react when the Signal changes.
Source: https://github.com/angular/angular/issues/56773#issuecomment-2198479276
Mitigation
1. Assign on the first line always
To register dependencies, assign from every Signal you use on the first line. Simplest approach, but easy to miss because it depends on the author’s attention.
effect(() => {
+ const count = this.count();
if (this.insideSimpleEffect === 5) {
- this.insideCondition = this.count();
+ this.insideCondition = count;
}
});
2. Use an IIFE
In an effect, call an IIFE and pass Signals as arguments to register dependencies. I prefer this—it makes the effect’s purpose clearer.
effect(() => ((count: number) => {
if (this.insideSimpleEffect === 5) {
this.insideCondition = count;
}
})(this.count()));
Summary
Signals are very ergonomic, especially for form change detection. Without understanding this behavior, they become a source of bugs, so I recommend codifying rules for how you use them.
See you next time.