skills/dev-skills/angular-developer/references/effects.md
effect and afterRenderEffectIn Angular, an effect is an operation that runs whenever one or more signal values it tracks change.
effectEffects are intended for syncing signal state to imperative, non-signal APIs.
Valid Use Cases:
localStorage or sessionStorage.<canvas> or 3rd-party charting library.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));
});
}
}
afterRenderEffectStandard 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):
earlyReadwrite (Never read here)mixedReadWrite (Avoid if possible)read (Never write here)Note: afterRenderEffect only runs on the client, never during Server-Side Rendering (SSR).