Post

Modern Angular 07: Effects

Modern Angular 07: Effects

This is lesson 7 of the Modern Angular Course. In the previous lessons, we covered writable signals for state and computed signals for derived values. Now we complete the signals trio by introducing effects — the way Angular handles side effects in a reactive, predictable way.

This post is part of the Modern Angular Course series. Check the course page for the full episode list.

In this post, we cover:

  • Why effects exist and what problem they solve
  • What an effect is and how it works
  • How to create your first effect
  • How effects differ from computed signals
  • Common use cases and common mistakes
  • Effect lifecycle and cleanup

Why Effects Exist

So far, our signals have been focused on state and values. Writable signals hold state. Computed signals derive values from that state. But in real applications, we often need to react to state changes in ways that go beyond calculating a value.

Examples:

  • Logging when a value changes
  • Syncing state to localStorage
  • Triggering analytics events
  • Calling an imperative third-party API

These are side effects — things that interact with the outside world. Effects exist to handle exactly this.

What Is an Effect?

An effect is a function that:

  • Runs whenever one of the signals it reads changes
  • Is used for side effects, not for deriving values
  • Does not return a value

You do not call an effect manually. Angular runs it automatically when its dependencies change. Just like computed signals, Angular tracks which signals an effect reads and re-runs it when those signals update.

Creating Your First Effect

Let’s add an effect to our Hello component. First, import effect from @angular/core:

1
import { Component, computed, effect, signal } from '@angular/core';

Now define an effect that logs whenever count changes:

1
2
3
4
5
protected count = signal(0);

private readonly countLog = effect(() => {
  console.log('Count changed:', this.count());
});

Every time count changes — whether by incrementing, decrementing, or resetting — this effect runs and logs the new value to the console. Angular automatically tracks the dependency on count().

Notice the pattern: the effect is declared as a private readonly field. It is private because no other class or template needs access to it. It is readonly because the effect reference should not be reassigned.

Effects vs Computed Signals

It is important to understand when to use each:

Computed signals:

  • Represent derived state
  • Return a value
  • Should be pure and side-effect free

Effects:

  • React to state changes
  • Perform side effects
  • Do not return values

A simple rule: if you are trying to calculate something, use a computed signal. If you need to do something in response to a change, use an effect.

Common Use Cases for Effects

Effects are great for:

  • Logging and debugging — track state changes during development
  • Persisting state — sync values to localStorage or sessionStorage
  • Third-party integrations — call imperative APIs that are not signal-aware
  • Analytics — trigger events when specific state changes

Effects are not meant to replace business logic, derived values, or state modeling. Think of effects as the bridge between reactive state and the outside world.

Common Mistakes with Effects

Effects are powerful, so a few cautions:

  • Do not update signals inside an effect unless you really know what you are doing — this can lead to infinite loops
  • Avoid complex logic inside effects — keep them small and focused on a single side effect
  • Do not use effects for rendering logic — that is what templates and computed signals are for

Misusing effects can lead to hard-to-debug behavior and unclear data flow. When in doubt, ask yourself: “Am I calculating a value or performing a side effect?” If it is the former, use a computed signal instead.

Effect Lifecycle and Cleanup

Effects are tied to the component lifecycle. When the component is destroyed, Angular automatically cleans up the effect. You do not need to manually unsubscribe like you would with RxJS subscriptions.

This makes effects safer and easier to manage for local component behavior. Create them, let them react, and Angular handles the rest.

The Complete Component

Here is the full component with all three signal types — writable signals, computed signals, and effects:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import { Component, computed, effect, signal } from '@angular/core';

@Component({
  selector: 'app-hello',
  imports: [],
  templateUrl: './hello.html',
  styleUrl: './hello.scss',
})
export class Hello {

  protected title = 'Welcome to Modern Angular!';

  protected isDisabled = false;

  protected onClick() {
    console.log('Button clicked');
    this.isDisabled = !this.isDisabled;
  }

  protected count = signal(0);

  protected doubleCount = computed(() => this.count() * 2);

  private readonly countLog = effect(() => {
    console.log('Count changed:', this.count());
  });

  protected increaseCounter() {
    this.count.update(value => value + 1);
  }

  protected decreaseCounter() {
    this.count.update(value => value - 1);
  }

  protected resetCounter() {
    this.count.set(0);
  }
}

With this component, we now have the full signals mental model in action:

  • count is a writable signal that holds state
  • doubleCount is a computed signal that derives a value from count
  • countLog is an effect that logs whenever count changes

Source Code

The full source code for the course is available on GitHub: loiane/modern-angular.

Next Step

Now that we have the full signals mental model — writable signals, computed signals, and effects — it is time to start building real features. In the next lesson, we add Angular Material and set up a reusable header component as the foundation for our project.

Watch the Video

This post is part of the Modern Angular Course series. Check the course page for the full episode list.

This post is licensed under CC BY 4.0 by the author.
This site uses cookies. Please choose whether to accept analytics cookies. Privacy Policy