Angular
Tutorial Angular: Effects en Angular
Aprenda a manejar efectos secundarios de forma reactiva utilizando los Effects. Guía detallada sobre su creación, uso y control del ciclo de vida.
Aprende Angular GRATIS y certifícate¿Qué son los Effects?
Los Effects en Angular son una característica introducida en la versión 16 que permite ejecutar código de forma reactiva en respuesta a cambios en signals u otras fuentes de datos observables. Forman parte del nuevo sistema de reactividad de Angular y están diseñados para manejar efectos secundarios de manera declarativa y eficiente.
Un Effect es una función que se ejecuta automáticamente cuando sus dependencias cambian, pero a diferencia de los computeds, no produce un nuevo valor. En su lugar, realiza operaciones que tienen efectos secundarios, como actualizar el DOM, realizar llamadas a APIs, o interactuar con servicios del navegador.
Una característica importante de los Effects es que se ejecutan de forma asíncrona durante el proceso de detección de cambios. Esto significa que Angular agrupa múltiples cambios y ejecuta los efectos en el próximo microtask, lo que puede mejorar el rendimiento al evitar ejecuciones innecesarias.
Crear y usar un Effect
Los Effects se definen utilizando la función effect()
proporcionada por Angular. Esta función toma como argumento una función de efecto que contiene el código a ejecutar. El sistema de reactividad de Angular rastrea automáticamente las señales y observables utilizados dentro de la función de efecto, y vuelve a ejecutar el efecto cuando cualquiera de estas dependencias cambia.
Para crear y usar un Effect en Angular, primero debemos importar la función effect
del paquete @angular/core
. Luego, podemos definir un Effect dentro de un componente o servicio de la siguiente manera:
import { Component, effect, signal } from '@angular/core';
@Component({
selector: 'app-example',
template: '<p>{{ message() }}</p>'
})
export class ExampleComponent {
message = signal('Hola');
constructor() {
effect(() => {
console.log(`El mensaje ha cambiado a: ${this.message()}`);
});
}
updateMessage() {
this.message.set('Hola Mundo');
}
}
En este ejemplo, hemos creado un Effect que se ejecutará cada vez que el valor de message
cambie. El Effect simplemente registra el nuevo valor en la consola.
Los Effects se ejecutan automáticamente una vez cuando se crean, y luego cada vez que cualquiera de sus dependencias (en este caso, message
) cambia. Es importante notar que los Effects no devuelven un valor, sino que realizan acciones secundarias.
Podemos crear Effects más complejos que dependan de múltiples señales o que realicen operaciones más elaboradas:
import { Component, effect, signal, inject } from '@angular/core';
import { JsonPipe } from '@angular/common';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-user',
standalone: true,
imports: [JsonPipe],
template: '<p>{{ userData() | json }}</p>'
})
export class UserComponent {
userId = signal(1);
userData = signal<any>(null);
private http = inject(HttpClient);
constructor() {
effect(() => {
this.http.get(`https://jsonplaceholder.typicode.com/users/${this.userId()}`)
.subscribe(data => this.userData.set(data));
});
}
updateUser(id: number) {
this.userId.set(id);
}
}
En este caso, el Effect se encarga de realizar una petición HTTP cada vez que userId
cambia, actualizando userData
con la respuesta.
También es posible pasar opciones al crear un Effect. Por ejemplo, podemos usar la opción allowSignalWrites
para permitir que el Effect modifique señales:
effect(() => {
const newValue = someComputation(this.inputSignal());
this.outputSignal.set(newValue);
}, { allowSignalWrites: true });
Es importante tener en cuenta que los Effects se ejecutan de forma asíncrona por defecto. Si necesitamos que un Effect se ejecute de forma síncrona, podemos usar la opción manualCleanup
:
effect(() => {
// Este efecto se ejecutará de forma síncrona
document.title = `User: ${this.userName()}`;
}, { manualCleanup: true });
Finalmente, los Effects se destruyen automáticamente cuando el componente o servicio que los contiene es destruido. Sin embargo, si necesitamos destruir un Effect manualmente, podemos hacerlo de la siguiente manera:
const cleanup = effect(() => {
// Lógica del efecto
});
// Más tarde, cuando queramos destruir el efecto:
cleanup();
Al crear y usar Effects, es importante considerar su impacto en el rendimiento y asegurarse de que se utilizan de manera apropiada para manejar efectos secundarios en nuestra aplicación Angular.
¿En qué casos se utiliza un Effect?
Los Effects en Angular son una herramienta poderosa para manejar efectos secundarios de manera reactiva, pero es importante utilizarlos en los casos apropiados. Algunos escenarios comunes donde los Effects son particularmente útiles incluyen:
- Sincronización con APIs externas: Cuando necesitas realizar llamadas a servicios externos en respuesta a cambios en el estado de la aplicación. Por ejemplo, enviar datos actualizados a un servidor cada vez que un formulario cambia.
- Persistencia de datos: Para guardar automáticamente el estado de la aplicación en el almacenamiento local o en una base de datos cuando ciertos valores cambian.
- Manipulación del DOM: Cuando necesitas realizar cambios en el DOM que no pueden manejarse fácilmente a través del sistema de templates de Angular. Por ejemplo, modificar atributos de elementos HTML o interactuar con bibliotecas de terceros que manipulan el DOM directamente.
- Navegación programática: Para implementar lógica de navegación compleja basada en cambios en el estado de la aplicación, como redirigir a un usuario después de que se complete una acción específica.
- Logging y analytics: Para registrar eventos o enviar datos de análisis en respuesta a cambios específicos en el estado de la aplicación.
- Gestión de suscripciones: Para manejar suscripciones a observables de manera reactiva, iniciando o cancelando suscripciones basadas en cambios en el estado.
- Actualización de metadatos de la aplicación: Por ejemplo, cambiar dinámicamente el título de la página o las metaetiquetas basándose en la ruta actual o el contenido visualizado.
- Caching y prefetching: Para implementar estrategias de caché o precarga de datos basadas en el comportamiento del usuario o cambios en el estado de la aplicación.
- Integración con APIs del navegador: Cuando necesitas interactuar con APIs del navegador como Geolocation, Web Storage, o Notifications en respuesta a cambios en el estado de la aplicación.
- Manejo de temporizadores y animaciones: Para iniciar, detener o modificar temporizadores o animaciones basándose en cambios en el estado de la aplicación.
Cuando no usar Effects
Se debe evitar el uso de Effects para la propagación de cambios de estado. Esto puede resultar en errores de ExpressionChangedAfterItHasBeenChecked
, actualizaciones circulares infinitas o ciclos innecesarios de detección de cambios. Estos problemas pueden afectar seriamente el rendimiento y la estabilidad de la aplicación.
Por ejemplo, no deberías usar Effects para actualizar un signal basado en el valor de otro signal. En su lugar, se recomienda usar computed signals para modelar estado que depende de otro estado. Esto permite a Angular manejar las actualizaciones de manera más eficiente y evita los problemas mencionados anteriormente.
Contexto de inyección para poder usar Effects
Para utilizar Effects en Angular, es crucial entender el contexto de inyección en el que operan. Los Effects están estrechamente ligados al sistema de inyección de dependencias de Angular y al ciclo de vida de los componentes y servicios.
Los Effects se pueden crear en cualquier lugar donde tengamos acceso al contexto de inyección de Angular. Esto incluye componentes, directivas y servicios. Sin embargo, el lugar donde creamos un Effect determina su ciclo de vida y su alcance.
En componentes y directivas, los Effects se crean típicamente en el constructor o en el método ngOnInit
. Estos Effects estarán vinculados al ciclo de vida del componente o directiva, y se destruirán automáticamente cuando el componente o directiva sea destruido.
Por ejemplo:
@Component({
selector: 'app-example',
template: '...'
})
export class ExampleComponent implements OnInit {
constructor() {
effect(() => {
// Este Effect está vinculado al ciclo de vida del componente
});
}
ngOnInit() {
effect(() => {
// Este Effect también está vinculado al ciclo de vida del componente
});
}
}
En servicios, los Effects pueden tener un alcance más amplio, dependiendo del alcance del servicio. Si el servicio es proporcionado a nivel de root
, los Effects creados en él existirán durante toda la vida de la aplicación. Si el servicio está proporcionado a nivel de módulo o componente, los Effects seguirán el ciclo de vida de ese módulo o componente.
@Injectable({
providedIn: 'root'
})
export class GlobalService {
constructor() {
effect(() => {
// Este Effect existirá durante toda la vida de la aplicación
});
}
}
Es importante tener en cuenta que los Effects necesitan acceso al contexto de inyección de Angular para funcionar correctamente. Esto significa que no podemos crear Effects fuera de las clases de Angular o en funciones estáticas.
Para casos especiales donde necesitamos crear Effects fuera del contexto normal de inyección, Angular proporciona la función inject()
. Esta función nos permite obtener el contexto de inyección actual y crear Effects de manera segura:
import { effect, inject } from '@angular/core';
function createCustomEffect() {
// Obtenemos el contexto de inyección actual
const injector = inject(Injector);
// Creamos el Effect utilizando el contexto de inyección
return effect(() => {
// Lógica del Effect
});
}
Para crear un Effect fuera del constructor, se puede pasar un Injector
a effect
via sus opciones:
@Component({...})
export class EffectiveCounterComponent {
readonly count = signal(0);
constructor(private injector: Injector) {}
initializeLogging(): void {
effect(() => {
console.log(`The count is: ${this.count()}`);
}, {injector: this.injector});
}
}
Este enfoque es útil cuando necesitas crear Effects en métodos que se llaman después de la inicialización del componente o en situaciones donde no tienes acceso directo al contexto de inyección.
Cuando trabajamos con Effects en pruebas unitarias, es importante tener en cuenta el contexto de inyección. Podemos usar TestBed
para configurar el entorno de pruebas y proporcionar los servicios necesarios:
import { TestBed } from '@angular/core/testing';
import { effect } from '@angular/core';
describe('EffectTest', () => {
beforeEach(() => {
TestBed.configureTestingModule({
// Configuración del módulo de pruebas
});
});
it('should create an effect', () => {
const component = TestBed.createComponent(MyComponent).componentInstance;
expect(() => effect(() => {})).not.toThrow();
});
});
Entender el contexto de inyección para los Effects nos permite utilizarlos de manera efectiva y segura en nuestras aplicaciones Angular, asegurándonos de que se creen y destruyan correctamente según el ciclo de vida de los componentes y servicios en los que se utilizan.
Destruir Effects
Los Effects en Angular se destruyen automáticamente cuando el componente o servicio que los contiene es destruido. Sin embargo, en algunas situaciones, puede ser necesario destruir un Effect manualmente antes de que su contexto de inyección sea destruido. Esto es particularmente útil cuando se necesita un control más granular sobre el ciclo de vida de los Effects o cuando se crean Effects dinámicamente.
Para destruir un Effect manualmente, podemos utilizar la función de limpieza que devuelve la función effect()
. Esta función de limpieza, cuando se invoca, detiene la ejecución del Effect y lo desvincula de sus dependencias.
Aquí tienes un ejemplo de cómo crear y destruir un Effect manualmente:
import { Component, effect, EffectRef, inject, Injector, runInInjectionContext, signal } from '@angular/core';
@Component({
selector: 'app-example',
template: '
<button (click)="toggleEffect()">{{ effectActive() ? 'Destroy' : 'Create' }} Effect</button>
<button (click)="incrementCounter()">Increment Counter ({{ counter() }})</button>
'
})
export class ExampleComponent {
counter = signal(0);
effectActive = signal(false);
private effectRef: EffectRef | null = null;
private injector = inject(Injector);
toggleEffect() {
if (this.effectActive()) {
this.destroyEffect();
} else {
this.createEffect();
}
this.effectActive.update(active => !active);
}
private createEffect() {
runInInjectionContext(this.injector, () => {
this.effectRef = effect(() => {
console.log(`Counter value: ${this.counter()}`);
});
});
console.log('Effect created');
}
private destroyEffect() {
if (this.effectRef) {
this.effectRef.destroy();
this.effectRef = null;
console.log('Effect destroyed');
}
}
incrementCounter() {
this.counter.update(value => value + 1);
}
ngOnDestroy() {
this.destroyEffect();
}
}
Este ejemplo demuestra cómo:
- Crear manualmente un efecto cuando se hace clic en el botón "
Create Effect
". - Destruir manualmente el efecto cuando se hace clic en el botón "
Destroy Effect
". - El efecto registra el valor del contador cada vez que cambia, pero solo cuando el efecto está activo.
- El efecto se limpia adecuadamente cuando el componente se destruye.
Es importante tener en cuenta que la destrucción manual de efectos debe usarse con cuidado, ya que puede llevar a comportamientos inesperados si no se maneja correctamente. En la mayoría de los casos, es preferible dejar que Angular maneje el ciclo de vida de los efectos automáticamente.
Para Effects que necesitan ser creados y destruidos dinámicamente, puedes mantener una colección de funciones de limpieza:
private effectCleanups: (() => void)[] = [];
createDynamicEffect() {
const cleanup = effect(() => {
// Lógica del Effect
});
this.effectCleanups.push(cleanup);
}
destroyAllEffects() {
this.effectCleanups.forEach(cleanup => cleanup());
this.effectCleanups = [];
}
ngOnDestroy() {
this.destroyAllEffects();
}
Este enfoque te permite crear múltiples Effects dinámicamente y asegurarte de que todos se destruyan adecuadamente cuando sea necesario.
Recuerda que la destrucción manual de Effects es una operación avanzada y, en la mayoría de los casos, permitir que Angular maneje automáticamente el ciclo de vida de los Effects es suficiente y más seguro. Solo deberías considerar la destrucción manual cuando tengas requisitos específicos que no puedan ser satisfechos por el comportamiento predeterminado de Angular.
Se puede usar la opción manualCleanup
para crear Effects que duran hasta que se destruyen manualmente, pero se debe tener cuidado de limpiarlos cuando ya no sean necesarios.
Ejercicios de esta lección Effects en Angular
Evalúa tus conocimientos de esta lección Effects en Angular con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Signals en Angular
Guards funcionales
Decodificar JWT en Angular
Servicio con HttpClient
Ciclo de vida de componentes en Angular
Gestión de productos de Fake Store API
Data binding en Angular
Routes sin módulos en Angular
Router en Angular
Instalación de Angular
Route Guards basados en interfaces
La directiva @if en Angular
Formularios reactivos en Angular
Servicios en Angular
Interceptor funcional
Servicio con Array
La directiva @for en Angular
Interceptores HTTP
Componentes standalone true
Formularios con ngModel en Angular
Routes en Angular
Comunicación entre componentes Angular
Parámetros en rutas con ActivatedRoute
CRUD de Restaurantes y Platos
Tablas en Angular Material
Formulario de registro de usuarios
Instalación y uso de NgBoostrap
Desarrollo de componentes Angular
JWT en Angular
Formularios reactivos en Angular
Formularios en Angular Material
Layout con Angular Material
Effects en Angular
Data binding
HttpClient en servicios de Angular
Desarrollo de módulos Angular
Comandos Angular CLI
Subir archivo en formularios
La directiva routerLink en Angular
Todas las lecciones de Angular
Accede a todas las lecciones de Angular y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Instalación Angular
Introducción Y Entorno
Comandos Angular Cli
Introducción Y Entorno
Desarrollo De Componentes Angular
Componentes
Data Binding En Angular
Componentes
Ciclo De Vida De Componentes En Angular
Componentes
Comunicación Entre Componentes Angular
Componentes
La Directiva @If En Angular
Componentes
La Directiva @For En Angular
Componentes
Componentes Standalone
Componentes
Desarrollo De Módulos Angular
Módulos
Routes En Angular
Enrutado Y Navegación
Router En Angular
Enrutado Y Navegación
La Directiva Routerlink En Angular
Enrutado Y Navegación
Parámetros En Rutas Con Activatedroute
Enrutado Y Navegación
Routes Sin Módulos En Angular
Enrutado Y Navegación
Servicios En Angular
Servicios E Inyección De Dependencias
Httpclient En Servicios De Angular
Servicios E Inyección De Dependencias
Formularios Con Ngmodel En Angular
Formularios
Formularios Reactivos En Angular
Formularios
Subir Archivo En Formularios
Formularios
Layout Con Angular Material
Integración Con Angular Material
Tablas En Angular Material
Integración Con Angular Material
Formularios En Angular Material
Integración Con Angular Material
Instalación Y Uso De Ngboostrap
Integración Con Bootstrap Css
Signals En Angular
Signals Y Reactividad
Effects En Angular
Signals Y Reactividad
Route Guards Basados En Interfaces
Autenticación Y Autorización
Guards Funcionales
Autenticación Y Autorización
Interceptores Http Basados En Interfaz
Autenticación Y Autorización
Interceptores Http Funcionales
Autenticación Y Autorización
Seguridad Jwt En Angular
Autenticación Y Autorización
Decodificar Tokens Jwt En Angular
Autenticación Y Autorización
Certificados de superación de Angular
Supera todos los ejercicios de programación del curso de Angular y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.
En esta lección
Objetivos de aprendizaje de esta lección
Comprender qué son los Effects en Angular.
Aprender a crear y usar effects en componentes y servicios.
Controlar el ciclo de vida de los effects, incluyendo su destrucción manual.
Utilizar el contexto de inyección para trabajar con effects.