← Angular: componentes, reatividade e decisões de aplicação
04 / 10 · 65 MIN

Ciclo de vida, efeitos e limpeza reativa

Relacionar inicialização, mudanças de inputs e destruição com recursos reativos. Escolher computed, effect e callbacks de renderização conforme a responsabilidade, e definir quem cancela trabalho pendente.

Inputs e inicialização não são o mesmo evento

ngOnInit executa uma vez depois de Angular inicializar os inputs do componente. Quando ngOnChanges também está implementado, a primeira chamada ocorre antes de ngOnInit. Mudanças posteriores de inputs podem originar novas chamadas ngOnChanges, sem repetir a inicialização única.

SimpleChanges permite consultar valor anterior, valor atual e se é a primeira mudança. Se o input value tem alias publicValue, a chave do registo continua a ser o nome da propriedade TypeScript value. Um alias usado no template não renomeia a API interna de observação.

Destruição e responsabilidade de limpeza

DestroyRef permite registar callbacks com onDestroy perto do local que prepara o recurso. Quando é injetado no âmbito de um componente ou diretiva, a limpeza acompanha a destruição desse proprietário; noutros âmbitos acompanha o injector correspondente.

O valor devolvido por onDestroy é uma função para retirar o registo do callback. Chamá-la não destrói o componente nem executa a limpeza registada. Esta diferença importa quando um recurso termina antes do proprietário: a aplicação pode libertá-lo e retirar um callback que já não deve ser executado, conforme o contrato desse recurso.

Derivar valores antes de criar efeitos

computed expressa um valor calculado a partir de signals. No exemplo, label depende de selected e não precisa de outro signal gravável sincronizado através de effect. Effects são úteis quando leituras reativas devem provocar trabalho imperativo numa API não reativa, como a notificação local apresentada.

O efeito rastreia as leituras efetivamente realizadas na sua execução; não é uma lista automática de todas as propriedades da classe. Duplicar estado derivado introduz mais pontos de escrita e pode criar ciclos ou momentos de incoerência. Primeiro identifica se precisas de um valor ou de uma ação.

Limpar antes da próxima execução

A função passada a effect recebe onCleanup, que regista trabalho a executar antes de o efeito voltar a correr ou ser destruído. No exemplo, cada execução captura o seu id e o seu timer. Uma nova execução cancela o timer anterior ainda pendente, evitando a notificação antiga nesse caso.

O cancelamento não retira mensagens já emitidas. Effects criados no contexto do componente são normalmente destruídos com ele. Se optares por manualCleanup: true, assumes a responsabilidade de conservar o EffectRef e chamar destroy quando o contrato terminar.

Guardar apenas a referência sem a destruir não satisfaz essa responsabilidade.

Subscrições precisam de um âmbito

takeUntilDestroyed associa a conclusão de uma subscrição à destruição do âmbito indicado. Dentro de um contexto de injeção, pode obter DestroyRef implicitamente. Num método acionado por um evento, esse contexto normalmente já não existe: conserva uma referência obtida num initializer e passa-a explicitamente a takeUntilDestroyed.

Isto evita transformar um mecanismo de limpeza num erro de resolução DI. A duração escolhida também interessa: usar um DestroyRef de uma instância mais longa pode manter a subscrição depois do componente consumidor desaparecer. Define o proprietário do recurso antes de escolher a referência.

Trabalho que depende do DOM atualizado

Um effect normal não é uma garantia de execução depois da atualização do DOM. Para uma integração reativa que precisa desse estado, considera afterRenderEffect conforme o contrato documentado. afterNextRender e afterEveryRender registam callbacks após a renderização da aplicação, e devem ser chamados num contexto de injeção.

Estes callbacks não são executados em renderização no servidor ou prerender. Não os uses como único local de uma transformação necessária à resposta do servidor. Distingue cálculo de dados, observação de inputs e acesso a elementos renderizados antes de escolher um hook pelo seu nome.

Respostas tardias e limites do cancelamento

DestroyRef.destroyed permite verificar se o proprietário já terminou antes de aplicar uma atualização tardia. Esta verificação é útil quando um callback assíncrono pode chegar após a destruição, mas não cancela automaticamente a Promise ou a operação remota.

Uma resposta de sucesso HTTP também não prova que a instância local ainda existe. Na revisão de um incidente, regista separadamente início da operação, destruição local, receção do resultado e decisão de o aplicar ou ignorar. Assim, a explicação da limpeza permanece fiel ao que foi interrompido e ao que ainda pode ter produzido efeitos.

// Reading example: synthetic selection notifications; no application bootstrap.
import {Component, computed, effect, signal} from '@angular/core';

@Component({
  selector: 'selection-notice', standalone: true,
  template: `<p>{{ label() }}</p>
    <button type="button" (click)="toggle()">Change selection</button>`,
})
export class SelectionNotice {
  readonly selected = signal('A');
  readonly label = computed(() => 'Selected: ' + this.selected());
  constructor() {
    effect(onCleanup => {
      const id = this.selected();
      const timer = setTimeout(() => console.info('Selection notice:', id), 1000);
      onCleanup(() => clearTimeout(timer));
    });
  }
  toggle() { this.selected.update(id => id === 'A' ? 'B' : 'A'); }
}
NA PRÁTICA

label deriva da seleção sem duplicar estado gravável. O effect cria uma notificação local atrasada para o id que leu; onCleanup cancela esse timer antes da próxima execução ou da destruição. Se o timer já tiver disparado, limpar o identificador não apaga a mensagem emitida.

A ordem relativa de cliques, execução do efeito e timer é uma premissa que deve ser explicitada num cenário.

Armadilhas comuns

Usar ngOnInit para todas as mudanças posteriores; copiar valores derivados através de effects; assumir que um effect normal vê o DOM já atualizado; chamar inject implicitamente num handler; esquecer destroy em manualCleanup; atribuir rollback remoto à limpeza local.

Tópicos relacionados: Signals, inputs e contratos de outputs · Injeção de dependências e ciclo de vida dos serviços · HTTP, streams e aceitação de respostas

Leva esta ideia contigo

Escolhe o mecanismo pelo evento observado: inputs, dependências reativas, renderização ou destruição. Depois identifica a duração do recurso e a ação de limpeza. A limpeza impede trabalho futuro abrangido pelo seu contrato; não elimina efeitos que já ocorreram.

Criar conta

Referência: Input initialization and changes · Angular 22; standalone components