Side Effects with `effect` and `afterRenderEffect`
In Angular, an effect is an operation that runs whenever one or more signal values it tracks change.
In Angular, an effect is an operation that runs whenever one or more signal values it tracks change.
In Angular, an **effect** is an operation that runs whenever one or more signal values it tracks change.
Effects are intended for syncing signal state to imperative, non-signal APIs.
**Valid Use Cases:**
**CRITICAL RULE: DO NOT use effects to propagate state.** If you find yourself using `.set()` or `.update()` on a signal _inside_ an effect to keep two signals in sync, you are making a mistake. This causes `ExpressionChangedAfterItHasBeenChecked` errors and infinite loops. **Always use `computed()` or `linkedSignal()` for state derivation.**
Effects execute asynchronously during the change detection process. They always run at least once.
import { Component, signal, effect } from '@angular/core';
@Component({...})
export class Example {
count = signal(0);
constructor() {
// Effect must be created in an injection context (e.g., a constructor)
effect((onCleanup) => {
console.log(`Count changed to ${this.count()}`);
const timer = setTimeout(() => console.log('Timer finished'), 1000);
// Cleanup function runs before the next execution, or when destroyed
onCleanup(() => clearTimeout(timer));
});
}
}Standard `effect` runs _before_ Angular updates the DOM. If you need to manually inspect or modify the DOM based on a signal change (e.g., integrating a 3rd party UI library), use `afterRenderEffect`.
`afterRenderEffect` runs after Angular has finished rendering the DOM.
To prevent reflows (forced layout thrashing), `afterRenderEffect` forces you to divide your DOM reads and writes into specific phases.
import { Component, afterRenderEffect, viewChild, ElementRef } from '@angular/core';
@Component({...})
export class Chart {
canvas = viewChild.required<ElementRef>('canvas');
constructor() {
afterRenderEffect({
// 1. Read from the DOM
earlyRead: () => {
return this.canvas().nativeElement.getBoundingClientRect().width;
},
// 2. Write to the DOM (receives the result of the previous phase)
write: (width) => {
// NEVER read from the DOM in the write phase.
setupChart(this.canvas().nativeElement, width);
}
});
}
}**Available Phases (executed in this order):**
_Note: `afterRenderEffect` only runs on the client, never during Server-Side Rendering (SSR)._