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
| Angular | React (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 useEffect | Cleanup: cancel fetches, unsubscribe, clear timers |
| ngOnChanges(changes) | useEffect(() => {}, [dep]) | Re-executa quando deps mudam |
| ngAfterViewInit() | useEffect com useRef | Acesso ao DOM depois da renderização |
| ngAfterViewChecked() | useEffect (toda re-renderização) | useEffect sem deps ou com todas as deps |
| ngDoCheck() | Sem equivalente direto | Use 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.
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');
}
}"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.
@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);
}
}
}"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.
@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 });
}
}"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.
// 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>
);
}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.Referências: useEffect — React Docs ↗ · Lifecycle hooks — Angular Docs ↗