Por que o Next.js (React) não tem lifecycle e o angular tem?

Algo que me questinei ao vim do angular para next.js foi lifecycle que não existia no next.js, mas por qual razão?

No desenvolvimento web moderno, a forma como os frameworks e bibliotecas lidam com o ciclo de vida dos componentes é fundamental para a criação de interfaces dinâmicas e performáticas. Enquanto o Angular oferece uma série de hooks que permitem executar código em momentos específicos do ciclo de vida, o Next.js (baseado no React) adota uma abordagem simplificada com o uso do hook useEffect e outras técnicas. Neste artigo, exploraremos essas diferenças, suas implicações e como elas afetam o fluxo de trabalho dos desenvolvedores.


1. Ciclo de Vida no Angular

O Angular foi projetado para fornecer um controle refinado sobre o ciclo de vida dos componentes. Entre os principais hooks, destacam-se:

  • constructor: Inicializa a instância do componente, mas não tem acesso às propriedades da view.
  • ngOnChanges: Chamado sempre que há mudanças nos inputs do componente.
  • ngOnInit: Executado logo após a inicialização dos inputs; ideal para configurações iniciais.
  • ngDoCheck: Permite detectar e reagir a mudanças que o Angular não captura automaticamente.
  • ngAfterContentInit e ngAfterContentChecked: Lidam com o conteúdo projetado (através de <ng-content>).
  • ngAfterViewInit e ngAfterViewChecked: São executados após a renderização da view.
  • ngOnDestroy: Responsável pela limpeza de recursos antes da destruição do componente.

Por exemplo, veja um trecho simples usando ngOnInit e ngOnDestroy:

import { Component, OnInit, OnDestroy } from '@angular/core';

@Component({
  selector: 'app-exemplo',
  template: `<h1>{{titulo}}</h1>`
})
export class ExemploComponent implements OnInit, OnDestroy {
  titulo = 'Olá, Angular!';

  ngOnInit() {
    console.log('Componente Angular inicializado!');
    // Aqui você pode buscar dados ou configurar o componente.
  }

  ngOnDestroy() {
    console.log('Componente Angular será destruído!');
    // Limpeza de subscriptions ou listeners.
  }
}

Exemplo Visual do Ciclo de Vida do Angular

Diagrama do Ciclo de Vida do Angular
Diagrama do Ciclo de Vida do Angular

Fonte: Exemplo de diagrama extraído de artigos técnicos doc angular

Essa abordagem granular permite que os desenvolvedores executem operações em momentos precisos, inclusive antes da renderização completa do componente, garantindo maior controle e previsibilidade.


2. Ciclo de Vida no Next.js (React)

O Next.js, que utiliza o React como biblioteca de base, adota uma abordagem mais funcional e enxuta. No React, os componentes funcionais não possuem os diversos hooks de ciclo de vida presentes no Angular. Em vez disso, o gerenciamento dos efeitos colaterais é feito por meio do hook useEffect.

Como o useEffect Funciona

  • Execução Pós-Renderização:
    O useEffect é executado após a renderização do componente. Assim, quaisquer atualizações de estado ou chamadas assíncronas disparadas dentro dele ocorrerão somente depois do componente ser exibido na tela.
    import { useEffect } from "react";
    
    function MeuComponente() {
      useEffect(() => {
        console.log("Componente montado!");
        
        // Função de cleanup (equivalente ao ngOnDestroy)
        return () => {
          console.log("Componente desmontado!");
        };
      }, []); // Array de dependências vazio: executa apenas uma vez
    
      return <div>Olá, mundo!</div>;
    }
    
  • Reexecução com Dependências:
    Se o array de dependências do useEffect incluir variáveis, o efeito será reexecutado sempre que essas variáveis mudarem. Isso pode ser uma vantagem para sincronizar estados, mas exige cautela para evitar execuções indesejadas.

Exemplo Visual do Fluxo do useEffect

Diagrama do Fluxo do useEffect no React
Fonte: Exemplo de diagrama ilustrativo dos Hooks do React artigo Embora essa abordagem simplificada torne o código mais conciso, ela possui a limitação de não permitir a execução de código antes da renderização, o que pode ocasionar um breve flash de conteúdo ou estado incompleto na interface.


3. Principais Diferenças e Implicações

Momento de Execução

  • Angular:
    Permite a execução de código antes e durante a renderização, usando hooks como o constructor, ngOnChanges e ngOnInit. Essa flexibilidade possibilita a preparação dos dados e estados antes que o usuário veja a interface.

  • Next.js/React:
    O useEffect roda após a renderização inicial. Embora haja maneiras de contornar isso (como renderização condicional e data fetching no servidor), não há um equivalente direto a um hook que seja executado antes da renderização.

Granularidade e Controle

  • Angular:
    Oferece uma série de hooks específicos para cada fase do ciclo de vida, permitindo uma separação clara das responsabilidades e facilitando a manutenção em aplicações complexas.

  • Next.js/React:
    A simplicidade do useEffect implica que muitas responsabilidades precisam ser gerenciadas com cuidado dentro de um único hook ou com a criação de múltiplos efeitos, o que pode levar a uma maior complexidade se não for bem organizado.

Data Fetching e SSR

  • Angular:
    Os desenvolvedores podem iniciar chamadas assíncronas nos hooks do ciclo de vida e ter maior controle sobre o timing dessas operações.

  • Next.js/React:
    Para garantir que os dados estejam disponíveis antes da renderização, o Next.js oferece funções como getStaticProps e getServerSideProps, que possibilitam a renderização do lado do servidor (SSR). Isso, no entanto, não substitui a lógica de ciclo de vida do componente renderizado no cliente.


5. Considerações Finais

A escolha entre Angular e Next.js (React) vai muito além da questão dos ciclos de vida. Cada abordagem possui suas vantagens:

  • Angular proporciona um controle fino e detalhado, ideal para aplicações robustas e complexas, onde a ordem e o timing da execução são críticos.
  • Next.js/React foca na simplicidade e na composição funcional, oferecendo uma curva de aprendizado mais suave e soluções modernas para server-side rendering e static site generation.

Compreender essas diferenças é fundamental para escolher a tecnologia que melhor se adapta aos requisitos do seu projeto e para evitar surpresas durante o desenvolvimento.

Na verdade o React tem hooks (ou algo parecido) também quando se usa components definidos como classes, porém essa abordagem foi descontinuada embora ainda seja suportada (está marcada como deprecated) talvez devido a sua alta complexidade.

Vide https://react.dev/reference/react/Component

Massa, vou da uma olhada ;)

O react até que tem uma forma melhor de lidar com isso, que teoricamente seria com o useLayoutEffect, que roda após a a mudança do componente mas antes da renderização. Vale dar uma olhada.