Creating Effects
Effects run side effects whenever tracked signal values change. They are the signal-based alternative to ngOnChanges and subscribe.
Basic Effect
import { Component, signal, effect } from '@angular/core';
@Component({
selector: 'app-counter',
template: `<p>Count: {{ count() }}</p>`,
})
export class CounterComponent {
count = signal(0);
constructor() {
effect(() => {
console.log('Count changed:', this.count());
});
}
}
Automatic Dependency Tracking
Effects automatically track which signals are read inside them:
constructor() {
effect(() => {
// This effect depends on count() and name()
console.log(`${this.name()} has count ${this.count()}`);
});
}
When count or name changes, the effect re-runs. No manual subscription needed.
Effect with Cleanup
constructor() {
effect((onCleanup) => {
const intervalId = setInterval(() => {
console.log('Count:', this.count());
}, 1000);
onCleanup(() => {
clearInterval(intervalId);
});
});
}
The onCleanup callback runs before the effect re-runs and when the effect is destroyed.
Practical Effect Patterns
Effects are useful for DOM manipulation, logging, syncing with external libraries, and triggering side effects.
DOM Manipulation
constructor() {
effect(() => {
document.title = `${this.count()} items`;
});
}
Syncing with External Libraries
constructor() {
effect(() => {
this.chart.update(this.data());
});
}
Logging and Debugging
constructor() {
effect(() => {
console.log('User changed:', this.user());
});
}
Effect in Injection Context
Effects must be created in an injection context (constructor, field initializer, or factory):
// GOOD: Constructor
constructor() {
effect(() => { ... });
}
// GOOD: Field initializer
private logger = effect(() => { ... });
// BAD: Not in injection context
someMethod() {
effect(() => { ... }); // Error!
}
Destroying Effects
private myEffect = effect(() => {
console.log('Value:', this.value());
});
// Later, to stop the effect
this.myEffect.destroy();
Effects created in a component constructor are automatically destroyed when the component is destroyed.
Effect vs Computed
| Feature | effect() | computed() |
|---|---|---|
| Returns | Nothing (void) | A signal value |
| Purpose | Side effects | Derived values |
| Read signals | Yes | Yes |
| Can write signals | Yes (careful!) | No (read-only) |
| Use for | DOM, logging, syncing | Derived state |
Quiz
1. What does effect() do?
2. How do you clean up an effect?
3. What is the difference between effect() and computed()?
Flashcards
Question
What does effect() do?
Click to reveal answer
Answer
Runs a side effect when tracked signals change. Auto-tracks dependencies.
Question
How do you destroy an effect?
Click to reveal answer
Answer
effect.destroy() — stops the effect. Auto-destroyed in components.
Question
effect() vs computed()?
Click to reveal answer
Answer
effect() runs side effects (void). computed() derives a signal value.
Revision Notes
Key Takeaways
- 1. effect() runs side effects when signals change
- 2. Dependencies are automatically tracked
- 3. effect.destroy() stops the effect
- 4. Use computed() for derived values, effect() for side effects
Interview Tips
- • Explain automatic dependency tracking
- • Be ready to describe effect cleanup
- • Mention the difference between effect() and computed()
Cheat Sheet
Effects Cheat Sheet
Create effect:
constructor() {
effect(() => {
console.log('Value:', this.count());
});
}
With cleanup:
effect((onCleanup) => {
const id = setInterval(() => {}, 1000);
onCleanup(() => clearInterval(id));
});
Destroy:
this.myEffect.destroy();
Rules:
- Must be in injection context
- Auto-destroyed in components
- Use for side effects, not derived values