🇺🇸 EN
Frontend
·3 min read·↗·🇺🇸 EN

Entendiendo InjectionContext y los effects de Signals

Portada principal del artículo: Entendiendo InjectionContext y los effects de Signals

Ayer estuve conversando con mi amigo @Jörgen de Groot sobre Signals. Cuando nuestra conversación pasó a hablar de los effects, se topó con el error ERROR NG0203.

Poco después, otro amigo me escribió con el mismo problema. Decidí armar un artículo corto para explicar por qué sucede.

Escenario

Tenemos una aplicación de contador que usa signals para registrar la cantidad de clics. Cuando la variable de signal count llega a 10, debemos actualizar una variable local normal (sin signals) con el texto 'You reached the limit of clicks'.

Usando Effect

El effect de Signals es muy útil para manejar efectos secundarios. En nuestro caso, tenemos un efecto secundario donde la variable 'limitMessage' necesita actualizarse cuando la variable 'count' llega a 10.

@Component({
  selector: 'my-app',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `
   <h1>{{limitMessage}}</h1>
    <h2>{{ count()}} times</h2>
 <button (click)="increase()">+1</button>
  `,
})
export class App  {
  count = signal(0);
  limitMessage = 'Feel free to click!';
 
  increase() {
    this.count.update((p) => p + 1);
  }
}
bootstrapApplication(App);

Ejecutamos el effect dentro del ciclo de vida ngOnInit para monitorear los cambios en la variable count.

export class App implements OnInit {
  count = signal(0);
  limitMessage = 'Feel free to click!';
  
  ngOnInit(): void {
    effect(() => {
      if (this.count() == 10) {
        this.limitMessage = 'You reached the limit, sorry';
      }
    });
  }

¡Rayos! ¡Apareció el error!

Effects e Injection Context

El effect debe ejecutarse dentro de un contexto de inyección (injection context), ya que utiliza el servicio DestroyRef para cancelar la suscripción cuando el bloque o componente se destruye.

El Constructor

La primera opción es mover el effect al constructor. Guardamos los cambios y ¡sí! ¡El effect funciona!

Usando runInjectionContext

Para usar el effect fuera del constructor, tenemos que utilizar la función runInInjectionContext, inyectando el Injector.

export class App implements OnInit {
  count = signal(0);
  limitMessage = 'Feel free to click!';
  disabled = signal(false);
  
  #injector = inject(Injector);
 
  ngOnInit(): void {
    runInInjectionContext(this.#injector, () => {
      effect(() => {
        if (this.count() == 10) {
          this.limitMessage = 'You reached the limit, sorry';
        }
      });
    });
  }
  increase() {
    this.count.update((p) => p + 1);
  }
}

¡Listo! ¡Tenemos el effect funcionando fuera del constructor de la clase y sin errores!

Actualizar Signals dentro de Effects

Ya estamos usando effects sin problemas, pero agreguemos una pequeña funcionalidad conectada a nuestro effect: quiero deshabilitar el botón cuando el contador llegue a 10.

Para esto, voy a crear la variable 'disabled' como una signal booleana y usar el effect para actualizar su valor.

export class App implements OnInit {
  count = signal(0);
  limitMessage = 'Feel free to click!';
  disabled = signal(false);
  #injector = inject(Injector);
 
  ngOnInit(): void {
    runInInjectionContext(this.#injector, () => {
      effect(() => {
        if (this.count() == 10) {
          this.limitMessage = 'You reached the limit, sorry';
          this.disabled.update(() => true);
        }
      });
    });
  }
  increase() {
    this.count.update((p) => p + 1);
  }
}

Guardamos los cambios e intentamos actualizar la signal disabled. ¡Maldición, otro error!

Por defecto, los effects no tienen permitido actualizar signals. Sin embargo, para estos casos podemos desactivar esta restricción configurando la opción allowSignalWrite en true.

¡Perfecto! Ya estamos trabajando con el effect de Signals sin ningún problema.

Resumen

Aprendimos cómo implementar el effect en el ciclo de vida ngOnInit, la importancia del contexto de inyección, cómo usar runInInjectionContext para evitar errores y cómo actualizar signals dentro de un effect desactivando la restricción por defecto con la opción allowSignalWrite.

¡Espero que esto te sirva cuando empieces a usar Signals y effects! ;)

Código fuente

Parte de la serie Frontend

Modern frontend development — Angular, React, TypeScript, accessibility, and performance.

Ver serie completa

Artículos Relacionados

Compartir este artículo

Si esta guía te resultó útil, compártela con tu equipo o con otros desarrolladores.


Software real. Lecciones reales.

Comparto las lecciones que aprendí por las malas, para que puedas evitarlas o estar preparado cuando ocurran.

User avatar
User avatar
User avatar
User avatar
+13K

Únete a 13,800+ desarrolladores y lectores.

Cero spam. Cancela tu suscripción en cualquier momento.