O ciclo de vida React descreve o que acontece quando um componente entra na interface, recebe novos dados e sai da tela. Esse modelo ajuda a responder perguntas práticas. Quando iniciar um timer? Quando sincronizar uma conexão? Em qual momento devemos remover um event listener?
Durante muito tempo, aprendemos essas etapas por meio dos métodos de componentes de classe. Hoje, componentes de função e Hooks são o padrão na maior parte dos projetos. No entanto, as classes continuam suportadas e aparecem em bases de código existentes. Por isso, vale entender os dois modelos.
O que é o ciclo de vida React
Um componente passa por três fases gerais:
- Montagem (mounting): o componente entra na árvore e aparece na interface.
- Atualização (updating): uma mudança em
props,stateoucontextprovoca uma nova renderização. - Desmontagem (unmounting): o componente sai da árvore.
Essa divisão ainda ajuda a explicar componentes. Porém, a documentação atual do React propõe outro modelo para Effects: começar e parar uma sincronização. Um Effect pode repetir esse ciclo várias vezes enquanto o componente continua montado.
Além disso, renderizar não significa necessariamente alterar o DOM. O React calcula a próxima interface e, durante o commit, aplica apenas as mudanças necessárias. Portanto, evite efeitos colaterais dentro da função de renderização.
Ciclo de vida com componentes de função
Em componentes de função, o Hook @@CODE0@@ sincroniza o componente com sistemas externos. Timers, conexões, APIs do navegador e bibliotecas que não pertencem ao React são exemplos comuns.
O código abaixo preserva a ideia do timer do artigo original, mas usa a API atual:
import { useEffect, useState } from 'react';
export function Relogio() {
const [agora, setAgora] = useState(() => new Date());
useEffect(() => {
const intervalId = window.setInterval(() => {
setAgora(new Date());
}, 1000);
return () => {
window.clearInterval(intervalId);
};
}, []);
return <p>Hora atual: {agora.toLocaleTimeString()}</p>;
}Code language: JavaScript (javascript)
Quando o componente entra na página, o Effect cria o intervalo. Depois, a função retornada faz o cleanup. O React executa essa limpeza antes de uma nova sincronização e, por fim, quando remove o componente.
Sem o clearInterval, o timer continuaria ativo mesmo após a remoção do relógio. O mesmo princípio vale para removeEventListener(), cancelamento de requisições e encerramento de conexões.
Como o array de dependências muda o Effect
O segundo argumento de useEffect controla quando a sincronização precisa acontecer novamente:
- Sem array, o Effect roda após cada commit.
- Com
[], ele não depende de valores reativos do componente. - Com
[valor], ele sincroniza novamente quandovalormuda.
Por exemplo, uma conexão com uma sala deve reagir à mudança de roomId:
useEffect(() => {
const connection = createConnection(roomId);
connection.connect();
return () => connection.disconnect();
}, [roomId]);Code language: JavaScript (javascript)
Antes de conectar à nova sala, o React encerra a conexão anterior. Dessa forma, setup e cleanup formam um único processo de sincronização.
Não remova dependências apenas para impedir uma nova execução. Use o linter do React e ajuste a estrutura do código. Se não existe um sistema externo para sincronizar, talvez você não precise de um Effect.
Por que o Effect pode rodar duas vezes no desenvolvimento
Com StrictMode, o React executa um ciclo extra de setup e cleanup durante o desenvolvimento. Esse teste ajuda a encontrar Effects sem limpeza adequada. Ele não acontece da mesma forma na build de produção.
Portanto, não use variáveis globais para bloquear a segunda execução. Torne o Effect seguro para iniciar, limpar e iniciar novamente. O usuário não deve perceber diferença entre esses ciclos.
Ciclo de vida em componentes de classe
Componentes de classe continuam válidos. Em muitos casos, três métodos concentram o trabalho relacionado a sistemas externos:
| Fase | Método | Uso comum |
|---|---|---|
| Montagem | componentDidMount() | Iniciar timer, assinatura ou integração |
| Atualização | componentDidUpdate() | Sincronizar após mudança de props ou state |
| Desmontagem | componentWillUnmount() | Remover listeners e liberar recursos |
O timer equivalente em classe fica assim:
import { Component } from 'react';
export class Relogio extends Component {
state = { agora: new Date() };
componentDidMount() {
this.intervalId = window.setInterval(() => {
this.setState({ agora: new Date() });
}, 1000);
}
componentWillUnmount() {
window.clearInterval(this.intervalId);
}
render() {
return <p>Hora atual: {this.state.agora.toLocaleTimeString()}</p>;
}
}Code language: JavaScript (javascript)
Para montar uma aplicação React atual, use createRoot no lugar da antiga chamada ReactDOM.render:
import { createRoot } from 'react-dom/client';
import { Relogio } from './Relogio.jsx';
createRoot(document.getElementById('root')).render(<Relogio />);Code language: JavaScript (javascript)
A referência de @@CODE0@@ documenta também métodos menos comuns. Entre eles estão shouldComponentUpdate(), getSnapshotBeforeUpdate() e getDerivedStateFromProps(). Use essas APIs apenas quando o problema realmente exigir esse controle.
Métodos antigos e APIs UNSAFE
Os métodos abaixo receberam o prefixo UNSAFE_ e não devem orientar código novo:
componentWillMount()componentWillReceiveProps()componentWillUpdate()
Eles pertencem a um modelo anterior e podem causar problemas com renderizações interrompidas ou repetidas. Em uma migração, revise o objetivo de cada método antes de procurar uma substituição direta.
Tratamento de erros com Error Boundaries
Erros de renderização em uma parte da árvore podem ser capturados por um Error Boundary. Em componentes de classe, esse padrão usa static getDerivedStateFromError() e, opcionalmente, componentDidCatch():
import { Component } from 'react';
export class ErrorBoundary extends Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, info) {
reportError(error, info.componentStack);
}
render() {
if (this.state.hasError) {
return <p>Não foi possível exibir este conteúdo.</p>;
}
return this.props.children;
}
}Code language: JavaScript (javascript)
Error Boundaries não capturam qualquer falha da aplicação. Por exemplo, erros em event handlers e código assíncrono precisam de tratamento no próprio fluxo. Frameworks e bibliotecas também podem oferecer abstrações para esse padrão.
Classes e Hooks: como comparar
Não existe um mapeamento perfeito entre cada lifecycle method e um Hook. Para muitos casos, a combinação de componentDidMount, componentDidUpdate e componentWillUnmount se aproxima de useEffect. Entretanto, o modelo mental é diferente.
Em uma classe, organizamos o código pela fase do componente. Com Hooks, organizamos cada sincronização de forma independente. Assim, a lógica de iniciar e encerrar uma conexão pode ficar no mesmo Effect.
Se você mantém um projeto antigo, preserve os componentes de classe que funcionam e migre quando houver um benefício claro. Para código novo, comece pelos componentes de função. Confira também os artigos sobre tipos de componentes React e como utilizar Hooks em React.

Próximos passos
O ciclo de vida continua útil para entender quando um componente entra, muda e sai da interface. No React moderno, porém, pense nos Effects como processos de sincronização. Cada processo deve saber iniciar, reagir às dependências e limpar os recursos que criou.
Como exercício, implemente o timer com useEffect e ative o StrictMode. Depois, remova o cleanup por alguns segundos para observar por que essa etapa é tão importante.

Deixe um comentário