Além do render
Um componente, em essência, calcula JSX a partir de props e estado. Mas às vezes você precisa fazer algo que não é calcular a interface: assinar um evento do navegador, iniciar um temporizador, mudar o título da aba, conversar com uma API... Isso é chamado de efeito colateral (side effect), porque afeta algo de fora do componente.
Essas ações não devem acontecer durante o render (o render deve ser puro e
pode ser executado várias vezes). Para isso existe o hook useEffect: ele diz
"execute este código depois de pintar, para sincronizar com algo externo".
function Titulo() {
const [n, setN] = useState(0);
useEffect(() => {
document.title = "Clicado " + n + " vezes";
});
return <button onClick={() => setN(n + 1)}>Clicar</button>;
}
useEffect recebe uma função (o efeito) que o React executa após renderizar.
O array de dependências
O segundo argumento de useEffect é um array que controla quando o
efeito é executado novamente:
// 1) Sem array: a CADA render
useEffect(() => { /* ... */ });
// 2) Array vazio []: apenas UMA vez, ao MONTAR o componente
useEffect(() => { /* ... */ }, []);
// 3) Com dependências: ao montar e toda vez que alguma delas MUDAR
useEffect(() => { /* ... */ }, [userId]);
[]→ "execute apenas ao montar". Ideal para configurar algo uma única vez (uma assinatura, um carregamento inicial de dados).[userId]→ "execute quandouserIdmudar". O React compara os valores do array entre renders; se algum mudou, ele reexecuta o efeito.
Regra de ouro: inclua nas dependências todo valor reativo (props, estado) que você use dentro do efeito. Se você omitir, vai trabalhar com dados velhos.
A função de limpeza
Muitas assinaturas precisam ser desfeitas: um temporizador precisa ser parado, um listener precisa ser removido. Para isso, o efeito pode retornar uma função de limpeza. O React a executa antes de reexecutar o efeito e ao desmontar o componente.
function Relogio() {
const [seg, setSeg] = useState(0);
useEffect(() => {
const id = setInterval(() => setSeg((s) => s + 1), 1000);
return () => clearInterval(id); // limpeza: para o intervalo
}, []);
return <p>{seg} s</p>;
}
Sem essa limpeza, o intervalo continuaria vivo após desmontar o componente: um vazamento de recursos (e possíveis erros). A limpeza evita assinaturas duplicadas e deixa o sistema como estava.
Enquanto você renderiza, pense: "o que precisa ser conectado?" (o efeito) e "o que precisa ser desconectado?" (a limpeza).