O ciclo de vida de um componente React tem três fases: montagem (o componente aparece na tela pela primeira vez), atualização (ele renderiza de novo porque o estado, as props ou o context mudaram) e desmontagem (ele é removido). Em componentes de função, você reage a essas fases com useEffect: um efeito com [] executa depois da montagem, um efeito com dependências executa depois das atualizações em que elas mudaram, e a função que um efeito retorna executa na desmontagem.
O console abaixo da prévia mostra cada fase quando ela acontece.
O console começa com render, count = 0 e mounted. Cada clique em Count registra uma renderização e uma atualização. Unmount registra unmounted; Mount de novo recomeça de count = 0, porque o estado foi descartado junto com o componente.
Montagem
Um componente monta na primeira vez que o React o renderiza em um determinado lugar da árvore. O React chama a função, cria os nós do DOM a partir do JSX que ela retorna, insere-os, deixa o navegador pintar a tela e então executa os efeitos do componente.
useEffect(fn, []) é onde você inicia coisas que devem existir enquanto o componente estiver na tela: uma assinatura, um timer, uma conexão, uma primeira requisição de dados. Ele executa depois da pintura, então nunca atrasa o primeiro frame.
Atualização
Um componente atualiza (renderiza de novo) quando:
- o próprio estado muda,
- o pai renderiza de novo (por padrão, todos os filhos também renderizam, tenham as props mudado ou não),
- um context que ele lê muda.
Depois de cada atualização, o React executa os efeitos cujas dependências mudaram. Antes de executar um efeito de novo, ele executa a limpeza anterior desse efeito, então a assinatura antiga fecha antes de a nova abrir. A página sobre renderização trata com mais detalhes do que dispara uma renderização.
Desmontagem
Um componente desmonta quando o pai para de renderizá-lo (como {show && <Counter />} faz acima), quando a key dele muda ou quando a própria raiz é desmontada. O React remove os nós do DOM dele, executa a limpeza de todos os efeitos e descarta o estado. Se o componente aparecer de novo depois, ele é uma instância totalmente nova.
Pais e filhos
O React renderiza de cima para baixo, mas executa os efeitos de baixo para cima. Quando um pai e um filho montam juntos, os efeitos do filho executam primeiro, então o efeito de um pai pode contar com os filhos já totalmente configurados.
A montagem registra render Parent, render Child, Child effect, Parent effect. Aperte Remove e as limpezas executam na ordem inversa: Parent cleanup e depois Child cleanup.
Fases de render e commit
Cada atualização passa por duas fases:
- Fase de render. O React chama os seus componentes para calcular o novo JSX e o compara com o resultado anterior. Nada muda na tela ainda. O React pode pausar esse trabalho, executá-lo duas vezes no StrictMode ou descartá-lo, e é por isso que a renderização precisa ser pura: nada de requisições, assinaturas ou mudanças em variáveis externas.
- Fase de commit. O React aplica as diferenças ao DOM, conecta as refs e executa as callbacks de
useLayoutEffect, tudo antes de o navegador pintar a tela. As callbacks deuseEffectexecutam depois disso, normalmente quando o navegador já pintou. Quando um clique ou outra entrada pontual causou a atualização, o React as executa logo depois dos layout effects, para que o resultado esteja pronto antes do próximo evento.
Cada clique registra as três linhas na mesma ordem. Esses efeitos não têm array de dependências, então executam depois de toda renderização. Adicione [] como segundo argumento aos dois e eles executam só depois da primeira.
Métodos de ciclo de vida de classe e seus equivalentes em hooks
Componentes de classe descrevem o ciclo de vida como métodos com nome. Você vai encontrá-los em código mais antigo e em error boundaries, que ainda precisam de uma classe.
| Método de classe | Quando executa | Equivalente em componente de função |
|---|---|---|
constructor | uma vez, antes da primeira renderização | useState(initial), useRef |
static getDerivedStateFromProps | antes de toda renderização | calcule o valor durante a renderização ou reinicie com uma key |
render | toda renderização | o corpo da função |
componentDidMount | depois que a primeira renderização está no DOM | useEffect(fn, []) |
shouldComponentUpdate | antes de uma atualização, pode pulá-la | React.memo (ele dá a resposta oposta: "as props são iguais") |
getSnapshotBeforeUpdate | logo antes de o DOM mudar | nenhum hook direto; useLayoutEffect com uma ref é o mais próximo |
componentDidUpdate | depois de cada atualização | useEffect(fn, [deps]) (também executa depois da montagem) |
componentWillUnmount | antes da remoção | a limpeza retornada pelo useEffect |
getDerivedStateFromError, componentDidCatch | quando um filho lança um erro | nenhum: escreva uma classe de error boundary |
Aqui está uma classe que registra cada método enquanto ele executa:
A montagem registra constructor, getDerivedStateFromProps, render, componentDidMount. Um clique registra getDerivedStateFromProps, shouldComponentUpdate, render, getSnapshotBeforeUpdate, componentDidUpdate. Faça shouldComponentUpdate retornar false e os cliques param de atualizar o botão: o estado muda, mas o React pula a renderização. A página sobre componentes de classe mostra como converter uma classe assim em uma função.
Pense em sincronização, não em ciclo de vida
A tabela de hooks é útil para ler código antigo, mas pode levar você a escrever efeitos que copiam hábitos de classes, como um grande efeito "did mount" e um grande efeito "did update". Um efeito se lê melhor como "mantenha esta coisa externa sincronizada com estes valores": conecte a roomId e, quando roomId mudar, desconecte e conecte de novo. Escreva um efeito para cada coisa que você sincroniza, liste os valores que ele usa como dependências e deixe o React decidir quando executá-lo.
Perguntas frequentes
Quais são as fases do ciclo de vida de um componente React?
Montagem (o React cria o componente e coloca o resultado dele na tela), atualização (ele renderiza de novo porque o estado, as props ou o context mudaram) e desmontagem (ele é removido e faz a limpeza).
Qual é o equivalente em hook do componentDidMount?
useEffect(() => { ... }, []). O array de dependências vazio faz com que ele execute uma vez, depois que a primeira renderização está na tela. A função que ele retorna é o equivalente do componentWillUnmount.
Qual é o equivalente em hook do componentDidUpdate?
useEffect com os valores que importam no array de dependências, como [userId]. Ao contrário do componentDidUpdate, ele também executa depois da primeira renderização, então cobre montagem e atualização juntas.
Em que ordem executam os efeitos do pai e do filho?
O React renderiza do pai para baixo, mas executa os efeitos dos filhos para cima: o efeito de um filho executa antes do efeito do pai. Na desmontagem, a limpeza do pai executa antes da do filho.
O que são as fases de render e commit?
Na fase de render, o React chama os seus componentes para descobrir como a tela deve ficar; ele pode pausar ou descartar esse trabalho. Na fase de commit, ele aplica as mudanças ao DOM e executa os layout effects; os efeitos comuns executam depois disso, normalmente quando o navegador já pintou a tela.