ngnext

Ciclo de Vida

Angular tem interfaces explícitas de lifecycle (OnInit, OnDestroy...). No React/Next.js, todos os efeitos colaterais e o ciclo de vida são gerenciados pelo hook useEffect — com uma diferença crucial para Server Components.

Tabela de equivalências

AngularReact (Client Component)Quando usar
constructor()Corpo da função (a cada render)Para inicializar refs, use useRef ou useState
ngOnInit()useEffect(() => {}, [])Executado uma vez após a montagem
ngOnDestroy()return () => {} dentro do useEffectCleanup: cancel fetches, unsubscribe, clear timers
ngOnChanges(changes)useEffect(() => {}, [dep])Re-executa quando deps mudam
ngAfterViewInit()useEffect com useRefAcesso ao DOM depois da renderização
ngAfterViewChecked()useEffect (toda re-renderização)useEffect sem deps ou com todas as deps
ngDoCheck()Sem equivalente diretoUse useSyncExternalStore para casos avançados

ngOnInit + ngOnDestroy → useEffect

O par ngOnInit / ngOnDestroy mapeia diretamente para um único useEffect: o corpo é o init, e a função de retorno é o destroy.

Angulardata.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';

@Component({ ... })
export class DataComponent implements OnInit, OnDestroy {
  data: Item[] = [];
  private sub?: Subscription;

  ngOnInit() {
    // Executado UMA VEZ após a montagem
    this.sub = this.dataService.items$.subscribe(
      (items) => (this.data = items)
    );
    console.log('Componente montado');
  }

  ngOnDestroy() {
    // Executado quando o componente é destruído
    this.sub?.unsubscribe();
    console.log('Componente destruído');
  }
}
Next.jsDataComponent.tsx
"use client";
import { useEffect, useState } from "react";

export default function DataComponent() {
  const [data, setData] = useState<Item[]>([]);

  useEffect(() => {
    // [] vazio = executa UMA VEZ após a montagem (ngOnInit)
    const sub = dataService.items$.subscribe(setData);
    console.log('Componente montado');

    // Função de cleanup = ngOnDestroy
    return () => {
      sub.unsubscribe();
      console.log('Componente destruído');
    };
  }, []); // <- array de dependências vazio

  return <ul>{data.map(i => <li key={i.id}>{i.name}</li>)}</ul>;
}

ngOnChanges → useEffect com dependências

No Angular, ngOnChanges é acionado quando um @Input() muda. No React, você lista as dependências no array do useEffect — o efeito re-executa quando qualquer delas mudar.

Angularuser.component.ts
@Component({ ... })
export class UserComponent implements OnChanges {
  @Input() userId!: string;
  user: User | null = null;

  ngOnChanges(changes: SimpleChanges) {
    // Executado toda vez que @Input muda
    if (changes['userId']) {
      this.userService
        .getUser(this.userId)
        .subscribe(u => this.user = u);
    }
  }
}
Next.jsUserProfile.tsx
"use client";
import { useEffect, useState } from "react";

type Props = { userId: string };

export default function UserProfile({ userId }: Props) {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    // Executado toda vez que userId muda (ngOnChanges)
    fetch("/api/users/" + userId)
      .then((r) => r.json())
      .then(setUser);
  }, [userId]); // <- dependência: re-executa quando userId muda

  if (!user) return <p>Carregando...</p>;
  return <p>{user.name}</p>;
}

ngAfterViewInit → useEffect com useRef

Para acessar o DOM após a renderização, o Angular usa @ViewChild + ngAfterViewInit. No React, você combina useRef com useEffect.

Angularchart.component.ts
@Component({ template: '<canvas #myCanvas></canvas>' })
export class ChartComponent implements AfterViewInit {
  @ViewChild('myCanvas') canvasRef!: ElementRef;

  ngAfterViewInit() {
    // DOM está disponível aqui
    const ctx = this.canvasRef.nativeElement.getContext('2d');
    new Chart(ctx, { type: 'bar', data: chartData });
  }
}
Next.jsChartComponent.tsx
"use client";
import { useEffect, useRef } from "react";

export default function ChartComponent() {
  const canvasRef = useRef<HTMLCanvasElement>(null);

  useEffect(() => {
    // Equivale ao ngAfterViewInit — DOM disponível aqui
    if (!canvasRef.current) return;
    const ctx = canvasRef.current.getContext('2d')!;
    new Chart(ctx, { type: 'bar', data: chartData });
  }, []);

  return <canvas ref={canvasRef} />;
}

Server Components: sem ciclo de vida

Server Components não têm ciclo de vida. Eles são funções async que rodam no servidor — não montam, não desmontam, não têm estado. Você simplesmente busca os dados diretamente na função e retorna o JSX.

app/user/[id]/page.tsx
// Server Component — SEM ciclo de vida
// Roda no servidor, não tem montagem/destruição no browser

async function getUser(id: string) {
  return await db.user.findUnique({ where: { id } });
}

export default async function UserPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  // Busca de dados diretamente — sem ngOnInit, sem useEffect
  const user = await getUser((await params).id);

  if (!user) return <p>Usuário não encontrado</p>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}
Regra de ouro: Se você precisa de useEffect, você precisa de 'use client'. Server Components não têm ciclo de vida porque não rodam no browser — eles são funções assíncronas comuns. Para buscar dados, use async/await diretamente.
Cuidado com useEffect para busca de dados: Se você está em um Client Component e precisa buscar dados, considere mover a busca para um Server Component pai e passar os dados como props. Isso é mais eficiente e simples.