Tiempo estimado de lectura: 4 min
Ideas clave
- Signals y primitivas reactivas reemplazan la mayoría de los lifecycle hooks clásicos.
- Convierte Inputs en señales y usa computed() para derivados; usa effect() para side-effects.
- Usa DestroyRef.onDestroy() para limpieza cerca del recurso, evitando ngOnDestroy globales.
- Reacciona al DOM con signal queries y afterNextRender() en lugar de los hooks after-view.
Tabla de contenidos
- Ideas clave
- Lifecycle hooks en Angular: qué dejar atrás y por qué
- Reglas prácticas de migración
- Por qué importa (y cuándo no cambiar)
- Lecturas recomendadas
- FAQ
Si llevas años programando en Angular, algunos hooks clásicos te resultarán familiares: ngOnChanges, ngOnInit, ngAfterViewInit, ngOnDestroy… La plataforma no elimina el lifecycle, pero las nuevas primitivas (Signals, DestroyRef y utilidades de renderizado) mueven el enfoque hacia relaciones declarativas entre datos y componentes reaccionando a cambios en señales en lugar de comprobar “cuándo ocurre X”.
Resumen rápido (lectores con prisa)
Qué: Signals y primitivas reactivas reemplazan la mayor parte de los lifecycle hooks clásicos.
Cuándo: Úsalos al convertir Inputs, calcular derivados y gestionar side-effects que dependan de estado reactivo.
Por qué: Código más declarativo, menos errores de timing y mayor testabilidad.
Cómo: Inputs → señales; derivados → computed(); efectos → effect(); limpieza → DestroyRef.onDestroy().
Lifecycle hooks en Angular: qué dejar atrás y por qué
ngOnChanges → computed()
ngOnChanges estaba destinado a detectar cambios en @Input() y recalcular derivados. Con Signals, transforma inputs en señales y usa computed() para expresar derivados de forma declarativa.
price = input.required<number>();
tax = input.required<number>();
total = computed(() => this.price() * (1 + this.tax()));
Por qué: menos código, cero bugs de sincronización, lecturas deterministas.
ngOnInit → constructor + effect() (o input())
ngOnInit fue el cajón de sastre. Los patrones modernos distribuyen responsabilidades:
- Estado inicial: declara
signal()ocomputed()en la propiedad. - Side-effects reactivos: usa
effect()para reaccionar a señales. - Carga inicial ligada a inputs: usa helpers que disparan requests cuando el input está disponible.
Sigue usando ngOnInit solo si necesitas un efecto que explícitamente ocurra una vez y no dependa de reactividad (migraciones, integraciones legacy).
ngDoCheck → casi siempre obsoleto
Si creías necesitar ngDoCheck, para y replantea. ngDoCheck suele esconder un mal diseño. Signals cubren la detección fina sin chequeos manuales. Rediseña con señales y computed(); si aún necesitas inspección manual, documenta por qué.
ngAfterViewInit / ngAfterContentInit → signal queries + afterNextRender
Acceder a un ViewChild o ContentChild sin esperar el momento correcto fue una fuente interminable de bugs. Las Signal Queries (viewChild, contentChild) devuelven señales que cambian cuando el elemento existe.
chartEl = viewChild<ElementRef>('chart');
constructor() {
effect(() => {
const el = this.chartEl();
if (el) { /* setup chart */ }
});
afterNextRender(() => {
// ejecutar una vez después del primer render en cliente
});
}
Combina con afterNextRender() para inicializar librerías del DOM (Chart.js, Leaflet) en cliente sin romper SSR.
ngAfterViewChecked → afterRender
Para reacciones tras cada render, afterRender() es la alternativa con semántica clara. Evita usarlo para lógica pesada; es para observabilidad y ajustes UI puntuales.
ngOnDestroy → DestroyRef
Olvida implementar OnDestroy en todas las clases. Inyecta DestroyRef y registra callbacks donde crees recursos. Esto vuelve la limpieza componible y cercana al lugar de creación del recurso.
const destroyRef = inject(DestroyRef);
const timer = setInterval(...);
destroyRef.onDestroy(() => clearInterval(timer));
Referencia: DestroyRef
Reglas prácticas de migración (lista corta, aplicable)
- Inputs → convierte a Signal Inputs; todos los cálculos derivados en
computed(). - Efectos → usa
effect()en constructor; evita suscripciones manuales cuando sea posible. - DOM post-render →
viewChild()+effect()para reactividad;afterNextRender()para inicializaciones DOM-only. - Limpieza →
DestroyRef.onDestroy()junto al recurso. - Legacy → mantén hooks si tu integración depende de ellos; documenta el porqué.
Por qué importa (y cuándo no cambiar)
Menos hooks = menos magia oculta. Código más testable. Menos errores por timing. Pero hay excepciones: bibliotecas legacy, patterns altamente instrumentados, o equipos que necesitan migraciones graduales. En esos casos, planifica parques de migración: componente por componente.
Angular evoluciona hacia un modelo declarativo. Adoptar Signals no es solo reescribir métodos; es reorganizar la arquitectura mental del componente: de “controlador de eventos” a “descripción de relaciones”. El resultado: componentes más predecibles, más fáciles de probar y mantener.
Lecturas recomendadas
FAQ
- ¿Por qué debería convertir mis Inputs a señales?
- ¿Cuándo sigue siendo válido usar ngOnInit?
- ¿Cómo reemplazo las suscripciones manuales?
- ¿Qué uso para inicializaciones que requieren el DOM?
- ¿Qué ventajas tiene DestroyRef frente a ngOnDestroy?
¿Por qué debería convertir mis Inputs a señales?
Convertir Inputs a señales permite expresar de forma declarativa los cálculos derivados con computed() y reaccionar con effect(). Esto elimina la necesidad de logic dispersa en ngOnChanges y reduce errores de sincronización.
¿Cuándo sigue siendo válido usar ngOnInit?
Usa ngOnInit cuando necesites un efecto que debe ejecutarse exactamente una vez y no depende de la reactividad de señales (por ejemplo, migraciones o integraciones legacy). Para lógica ligada a estado reactivo, prefiere effect().
¿Cómo reemplazo las suscripciones manuales?
Siempre que sea posible, reemplaza suscripciones con effect() o convierte fuentes externas en señales. Para recursos que aún usan callbacks o timers, registra limpieza con DestroyRef.onDestroy() junto al recurso.
¿Qué uso para inicializaciones que requieren el DOM?
Usa signal queries (viewChild(), contentChild()) que devuelven señales y combínalas con effect(). Para inicializaciones que deben ocurrir solo en cliente después del primer render, utiliza afterNextRender().
¿Qué ventajas tiene DestroyRef frente a ngOnDestroy?
DestroyRef permite registrar callbacks de limpieza cerca del recurso, haciendo la limpieza composable y localizada. Evita la necesidad de implementar la interfaz OnDestroy en cada clase y mejora la trazabilidad de recursos.

Leave a Reply