DEV Community

Migrando um YMS de 5 anos: Do Angular 16 ao 21 (Parte 5) - Reatividade Limpa: Deixando o ngOnDestroy no passado com Signals

Fala, comunidade dev! đź‘‹

Na nossa jornada modernizando um sistema corporativo de 5 anos (do Angular 16 para o 21), já limpamos o app.module.ts, modernizamos as rotas, abraçamos o inject() e enxugamos o HTML. Mas existe um fantasma que assombra projetos legados: os vazamentos de memória (memory leaks).

Em sistemas pesados com dezenas de formulários e grids, assinar eventos do RxJS (subscribe) sem limpá-los no ngOnDestroy é o caminho mais rápido para travar o navegador do usuário. Hoje, vamos ver como os Signals mudaram o jogo da reatividade, especialmente lidando com Formulários Reativos.


1. O Fantasma do RxJS em Formulários

Sistemas Enterprise têm formulários gigantescos. Imagine uma tela de cadastro com dezenas de campos, onde a mudança de uma opção (como selecionar "Pessoa Jurídica") dispara validações ou desabilita outras partes da tela.

No Angular 16, ouvíamos essas mudanças assim:

O Antes (Poluição visual e risco de vazamento):

@Component({ ... })
export class CadastroComponent implements OnInit, OnDestroy {
  private destroy$ = new Subject<void>();
  formulario: FormGroup;

  ngOnInit() {
    // Ouve as mudanças do formulário
    this.formulario.get('tipoEntidade')?.valueChanges
      .pipe(takeUntil(this.destroy$)) // O boilerplate de sempre...
      .subscribe(valor => {
        this.atualizarRegrasDeTela(valor);
      });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}
Enter fullscreen mode Exit fullscreen mode

A Bomba-Relógio: Multiplique esse boilerplate por 50 telas. É código repetitivo, propenso a falhas (quem nunca esqueceu um takeUntil ou esqueceu de implementar o ngOnDestroy?) e difícil de ler. Um único subscribe esquecido aberto fica rodando na memória RAM do navegador para sempre, mesmo após o usuário trocar de tela.


2. A Revolução dos Signals nos Formulários

Os Signals (signal(), computed(), effect()) introduziram uma reatividade síncrona, granular e livre de inscrições manuais. O Angular 21 permite transformar Observables (como o valueChanges do seu form) diretamente em Signals usando a função toSignal.

Olha como o mesmo cĂłdigo fica agora, sem implementar interfaces de ciclo de vida e sem o famigerado ngOnDestroy:

O Depois (Reativo, Limpo e Seguro):

@Component({ ... })
export class CadastroComponent {
  private fb = inject(FormBuilder);

  formulario = this.fb.group({
    tipoEntidade: ['']
  });

  // Transforma o Observable em um Signal reativo!
  tipoEntidadeSignal = toSignal(this.formulario.get('tipoEntidade')!.valueChanges);

  constructor() {
    // Reage automaticamente às mudanças. O Angular destrói isso sozinho!
    effect(() => {
      const valor = this.tipoEntidadeSignal();
      if (valor !== undefined) {
        this.atualizarRegrasDeTela(valor);
      }
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

A Mágica por baixo dos panos: O toSignal automaticamente se inscreve no Observable e, o mais importante, cancela a inscrição sozinho quando o componente é destruído. O effect rastreia suas dependências e só executa quando o valor realmente muda. Adeus, memory leaks!


ConclusĂŁo

Nós não abandonamos o RxJS. Ele continua sendo o rei absoluto para chamadas HTTP e fluxos assíncronos complexos baseados em eventos no tempo. Mas para gerenciamento de estado local da tela e formulários, os Signals removeram toneladas de código inútil do nosso projeto. O código ficou previsível, fácil de testar e livre de vazamentos de memória.

No prĂłximo artigo (Parte 6), vamos falar sobre o impacto direto dessa reatividade sĂ­ncrona: a possibilidade de finalmente desligarmos o motor antigo do Angular que causava tantos gargalos de performance, o zone.js. Vamos falar sobre o Zoneless Angular!

Você já começou a refatorar seus formulários velhos para usar Signals e toSignal? Conta aí a sua experiência! 👇

Top comments (0)