With Angular 16+, Signals introduce a fine-grained reactivity model that improves change detection by updating only the affected parts of the UI instead of triggering a full component tree check.
Why Use Signals for Change Detection?
Traditional Angular change detection relies on Zone.js to track changes, which can lead to unnecessary DOM updates. Signals offer a push-based approach, ensuring that only dependent components update when a signal value changes.
How Signals Work in Angular Change Detection
- Signals hold reactive values that notify consumers when updated.
- They automatically track dependencies, ensuring efficient updates.
- They reduce reliance on
ChangeDetectionStrategy.OnPushand manual optimizations likemarkForCheck().
Using Signals in Angular Components
Basic Example of Signals in a Component
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<h2>Counter: {{ count() }}</h2>
<button (click)="increment()">Increment</button>
`,
})
export class CounterComponent {
count = signal(0); // Define a signal
increment() {
this.count.set(this.count() + 1); // Update signal value
}
}
Here, count() acts like a reactive getter.
The template updates only when count changes, reducing unnecessary re-renders.
Computed Signals for Derived State
Computed signals let you create values that automatically update when dependencies change.
import { Component, signal, computed } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<h2>Counter: {{ count() }}</h2>
<h3>Double: {{ doubleCount() }}</h3>
<button (click)="increment()">Increment</button>
`,
})
export class CounterComponent {
count = signal(0);
doubleCount = computed(() => this.count() * 2); // Automatically updates when `count` changes
increment() {
this.count.set(this.count() + 1);
}
}
doubleCount() updates only when count changes, making it more efficient than manual recalculations.
Effect Signals for Side Effects
Effect signals allow running side effects when a signal changes.
import { Component, signal, effect } from '@angular/core';
@Component({
selector: 'app-timer',
template: `<h2>Time: {{ time() }}</h2>`,
})
export class TimerComponent {
time = signal(new Date().toLocaleTimeString());
constructor() {
effect(() => {
console.log('Time updated:', this.time());
});
setInterval(() => {
this.time.set(new Date().toLocaleTimeString()); // Updates every second
}, 1000);
}
}
The effect() function automatically tracks the signal and executes whenever time changes.
Signals provide a powerful, fine-grained, and efficient way to handle change detection in Angular, reducing reliance on Zone.js and manual optimizations. As Angular moves towards a zoneless future, adopting signals can lead to significant performance improvements.