Efeitos
Imagine um componente que precisa manter um timer contando após renderizar, como o componente Timer abaixo faz. A maior parte do que um componente faz acontece enquanto ele renderiza: ele lê props e state e retorna JSX. Manter um timer rodando é diferente. O mesmo vale para abrir uma subscription, sincronizar o título do documento, ou buscar dados de um servidor. Cada uma dessas ações alcança o mundo externo para tocar em algo que React não controla. useEffect é o hook para isso. Ele deixa um componente rodar código após renderizar, para que ele possa sincronizar a si mesmo com essa coisa externa.
Aqui está um efeito que inicia o timer uma vez e incrementa um contador a cada segundo:
import { useState, useEffect } from 'react'
function Timer() {
const [count, setCount] = useState(0)
useEffect(() => {
const id = setInterval(() => setCount(c => c + 1), 1000)
return () => clearInterval(id)
}, [])
return <p>{count}</p>
}Três partes estão fazendo o trabalho aqui, e elas correspondem às três coisas que todo efeito lida: o que fazer, como limpar e quando executar.
A função que você passa para useEffect é o efeito em si. Este chama setInterval para iniciar um timer repetido que incrementa count em um a cada segundo.
O array de dependências
O segundo argumento, o [] no final, é o array de dependências. Ele controla quando o efeito é executado.
- Um array vazio
[]significa que o efeito é executado uma vez, após o componente aparecer na tela pela primeira vez. O timer acima usa isso: inicia o intervalo uma única vez e deixa ele rodando. - Um array com valores, como
[a, b], significa que o efeito é executado após a primeira renderização e novamente sempre que um desses valores muda entre renderizações. Assim você ressincroniza quando algo do qual o efeito depende mudou. - Deixar o array de fora inteiramente significa que o efeito é executado após cada renderização. Isso raramente é o que você quer, e é uma fonte comum de loops descontrolados quando o efeito também atualiza state.
Então o array é sua resposta para a pergunta "quando esse código deve rodar novamente?" Liste os valores que o efeito lê e depende, e React o executa novamente quando qualquer um deles mudar.
Funções de limpeza
O efeito acima retorna uma função:
return () => clearInterval(id)Essa função retornada é a função de limpeza. React a executa antes do efeito rodar novamente, e mais uma vez quando o componente é removido da tela. Seu trabalho é desfazer o que o efeito configurou.
Limpeza importa porque efeitos frequentemente iniciam algo que continua rodando por conta própria: um intervalo, um event listener, uma conexão aberta. Se o componente é removido e nada para esse intervalo, ele continua disparando para sempre e mantém uma referência a um componente que se foi. Faça isso algumas vezes e você tem um vazamento lento e atualizações chegando a coisas que não existem mais. A regra de ouro: se um efeito inicia algo, se inscreve em algo, ou abre algo, sua função de limpeza deve parar, desinscrever-se ou fechar isso.
Você pode não precisar de um efeito
Efeitos servem para sincronizar com o mundo externo, então muito código que parece ser candidato para useEffect pertence a outro lugar. Dois casos aparecem constantemente.
O primeiro é valores derivados. Se você pode calcular algo a partir de props e state que você já tem, calcule durante a renderização:
function Cart({ items }) {
const total = items.reduce((sum, item) => sum + item.price, 0)
return <p>Total: {total}</p>
}total é derivado de items, então é computado a cada renderização e sempre está sincronizado. Armazená-lo em seu próprio state e atualizá-lo a partir de um efeito seria mais código e uma renderização extra sem ganho.
O segundo é responder a uma ação do usuário. Quando algo deve acontecer porque o usuário clicou ou digitou, esse código vai no event handler para essa ação:
function BuyButton({ product }) {
function handleClick() {
buyProduct(product)
}
return <button onClick={handleClick}>Buy</button>
}A compra acontece em handleClick, exatamente onde o clique é tratado. Roteiá-la através de um efeito adicionaria uma camada de indireção e tornaria o fluxo mais difícil de seguir. Um bom teste: se o código é executado por causa de uma interação específica, use um event handler; se é executado para manter o componente sincronizado com um sistema externo, use um efeito.
Version note
In class components this behavior was split across three lifecycle methods: componentDidMount for setup after the first render, componentDidUpdate for re-running when data changed, and componentWillUnmount for cleanup. A single useEffect with a dependency array and a cleanup function covers all three, which is why related setup and teardown code now lives together instead of being spread across separate methods.
useEffect como o lugar para código que alcança fora de React: um timer, o título do documento, uma requisição a um servidor. O array no final diz quando executar, [] significando uma vez, logo após o componente aparecer. E se seu efeito inicia algo, retorne uma pequena função que o para.
A maior parte das vezes você não vai precisar de um efeito, então é um bom hábito pausar e perguntar se um cálculo simples ou um click handler não faria o trabalho primeiro.
Próximo: Buscando dados, onde componentes trazem dados que vivem completamente fora de React.

