Search code examples
angularangular-signals

Does setting a writable signal to the same value as before mark a component as dirty?


I know that computed signals and effects are lazily evaluated and memoized. They don't do more work than needed. But if I do this in an Angular component:

derpSignal = signal("derp")

functionThatGetsCalledRandomly() {
  derpSignal.set("derp")
}

Does the component get marked dirty every time derpSignal gets set, independent of the previous value? Or does that happen only if the value changes?

Meaning, should I in this case wrap it in

if(derpSignal() !== "derp") {
  derpSignal.set("derp")
}

or is that check already done by the signal itself?


Solution

  • Best you try it out, from what I can see, only when the value changes the signal fires, but please note this important point

    Primitive types -> Number, String, Boolean, Undefined, Null and Symbol

    Signal fires when the actual value changes ( 1 != 2 -> change fires )

    Non-Primitive Types -> Object, Array, etc.

    Are stored as references in memory.

    Signal fires when the memory reference changes ( memref != memref2 change fires )

    Also when you change a nested property of a non primitive type, the reference does not change!

    Please check the below example, where setting the value does not trigger a change on either the effect or the computed signal!

    import { Component, computed, effect, signal } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import 'zone.js';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      template: `
        <h1>Hello from {{ name }}!</h1>
        <a target="_blank" href="https://angular.dev/overview">
          Learn more about Angular
        </a>
        {{computedSignal()}}
      `,
    })
    export class App {
      name = 'Angular';
    
      derpSignal = signal('derp');
      computedSignal = computed(() => {
        const signalData = this.derpSignal();
        console.log(signalData, 'changed computed');
      });
    
      constructor() {
        effect(() => {
          const signalData = this.derpSignal();
          console.log(signalData, 'changed effect');
        });
      }
    
      functionThatGetsCalledRandomly() {
        this.derpSignal.set('derp');
      }
    
      ngOnInit() {
        setInterval(() => {
          this.functionThatGetsCalledRandomly();
        }, 1000);
      }
    }
    
    bootstrapApplication(App);
    

    Stackblitz Demo