Aplicações React são construídas a partir de componentes, e cada componente vive uma vida. Ele aparece na tela. Ele reage a novas informações. Eventualmente, ele desaparece. Ignore esse ritmo e o preço será real. Vazamentos de memória (memory leaks) surgem. O desempenho de renderização degrada. Usuários clicam em botões que não respondem mais porque o componente associado a eles foi desmontado dez minutos atrás, mas o event listener ainda está vivo. Compreender o ciclo de vida do componente não é um exercício acadêmico. É a base para escrever software que permanece confiável sob uso real.
Pense na existência de um componente em três estágios. Desenvolvedores costumam descrevê-los como nascimento, crescimento e morte. O React os chama de mounting, updating e unmounting. Cada estágio tem um trabalho específico, e cada um oferece um momento preciso para executar código ou limpá-lo.
As Três Fases
Mounting é o nascimento. O componente é criado e inserido no DOM do navegador pela primeira vez. Esta é a sua janela de configuração. Se o componente precisar de dados remotos, é neste momento que você os solicita. Se precisar de um estado inicial calculado a partir de props, você faz isso aqui. Na era dos componentes de classe, este era o domínio do componentDidMount. Com os modernos componentes funcionais, você lida com isso usando useEffect e um array de dependências vazio. O detalhe crucial é que este bloco de código é executado uma única vez, imediatamente após o React colocar o elemento na página. Isso o torna ideal para configurações únicas, como buscar um perfil de usuário ou ler um valor do localStorage.
Updating é o crescimento. Um componente montado não fica parado. Props chegam de um componente pai. O estado muda devido a uma interação do usuário. O próprio pai renderiza novamente, forçando o filho a seguir. O React então renderiza novamente o componente para manter a UI sincronizada com seus dados. Esta fase se repete muitas vezes durante a vida de um componente. Toda vez que uma dependência muda, seus efeitos são executados novamente, e o componente injeta novos resultados no DOM. Como esta fase é disparada com tanta frequência, é também onde a maioria dos problemas de desempenho começa. O trabalho desnecessário aqui se acumula rapidamente.
Unmounting é a morte. O componente sai da tela, talvez porque o usuário navegou para uma página diferente ou uma renderização condicional o desligou. Neste ponto, o componente é destruído, mas o mundo exterior nem sempre sabe disso. Temporizadores continuam rodando. WebSockets continuam ouvindo. Event listeners no objeto window permanecem anexados. Se você não limpá-los, eles manterão referências a componentes mortos e causarão vazamento de memória. O navegador desperdiça ciclos com fantasmas. O React oferece uma função de limpeza (cleanup function) precisamente para que você possa parar intervalos, fechar conexões e remover listeners antes que o componente desapareça de vez.
De Métodos de Classe para Hooks
Por anos, engenheiros de React gerenciaram essas fases por meio de componentes de classe. A lógica era dividida entre métodos distintos. Você buscava dados dentro de componentDidMount, respondia a mudanças de props dentro de componentDidUpdate e "varria o chão" dentro de componentWillUnmount. O problema era que a lógica relacionada acabava espalhada. Um único recurso, como uma conexão de chat ao vivo, exigia código em três métodos de ciclo de vida separados apenas para conectar, verificar mudanças de thread e desconectar.
Os Hooks mudaram isso. O hook useEffect em componentes funcionais permite agrupar a lógica por propósito, em vez de por fase de ciclo de vida. Ainda assim, o modelo mental mapeia perfeitamente se você souber onde procurar.
Para lidar com o mounting, escreva useEffect(() => { ... }, []). O array de dependências vazio diz ao React que este efeito não tem dependências externas, portanto, ele é executado apenas após a renderização inicial. Isso substitui o componentDidMount.
Para lidar com updates, inclua os valores específicos que o React deve observar: useEffect(() => { ... }, [dependency]). Quando a dependency muda entre as renderizações,
