Segundo a própria documentação do React, useEffect
é um Hook do React que permite sincronizar um componente com um sistema externo.
Se você já codifica com React, sabe bem para o quê serve, mas, convenhamos, essa descrição mais resumida não explica tanta coisa.
Aprofundando um pouco mais na documentação sobre useEffect, constata-se que
Alguns componentes precisam ser sincronizados com sistemas externos. Por exemplo, você pode querer controlar um componente que não é React com base no estado do React, configurar uma conexão com o servidor ou enviar um log quando um componente aparece na tela. Effects permitem que você execute algum código após a renderização para que possa sincronizar seu componente com algum sistema externo ao React.
Quer dizer, O useEffect é um hook do React que permite realizar “efeitos colaterais” em componentes.
Em termos mais simples, ele serve para executar ações em momentos específicos do ciclo de vida de um componente, como por exemplo, quando o componente é montado, quando ele é atualizado ou quando ele é desmontado.
Maaas…
Mesmo que você já saiba o quê é e para o quê serve esse hook de React, pode ser que você esteja usando useEffect errado!
O React não é tão restritivo e deixa que suas features sejam usadas de maneira mais livre, propiciando que se possa chegar a objetivos de várias maneiras diferentes.
E essa liberdade, se não revisada/supervisionada, aliada a um entendimento equivocado sobre o que é e como exatamente o useEffect funciona, pode levar a casos de uso equivocados do hook.
Um meme sobre useEffect no Xwitter
Há não muito tempo atrás, vi esse meme no Xwitter (inclusive, segue lá), com texto “A equipe do React quando vê desenvolvedores usando useEffect“:
O brinquedo mostra blocos de formas diferentes sendo forçados a passar pelo buraco de um quadrado é uma metáfora visual para como o useEffect está sendo usado de forma “forçada” e não muito ideal.
Quer dizer, em vez de usar a “forma” correta (ou o hook adequado para a situação), muitos desenvolvedores simplesmente usam o useEffect para tudo — como tentar passar uma peça redonda ou triangular por um buraco quadrado.
Então, aqui estão 6 maneiras erradas de usar useEffect de React junto a alternativas mais apropriadas para o uso do hook, para você conferir se está usando corretamente ou não.
1. Usar useEffect quando nem é necessário
Um dos erros mais comuns é usar useEffect para inicializar ou atualizar estados simples, algo que pode (e deve) ser feito diretamente com useState ou lógica pura.
Exemplo ruim:
|
1 2 3 |
useEffect(() => { setCount(0); }, []); |
Aqui, acontece a inicialização do estado dentro de um useEffect, o que é redundante e desnecessário.
Esse tipo de código adiciona complexidade sem motivo.
Alternativa:
|
1 |
const [count, setCount] = useState(0); |
Use useEffect apenas quando for realmente necessário reagir a algo que acontece fora do React, como uma API, o navegador, ou uma dependência externa.
Para inicializações simples, useState é o suficiente.
2. Dependências mal configuradas (ou esquecidas)
O segundo erro mais comum é esquecer a lista de dependências do useEffect (ou configurá-la incorretamente).
Sem dependências, o efeito será executado a cada renderização, o que pode causar loops infinitos, degradação de performance e outras mazelas.
Vacilo no código:
|
1 2 3 |
useEffect(() => { console.log('Executou!'); }); |
Esse efeito roda em toda renderização, mesmo que nada tenha mudado.
Melhor:
|
1 2 3 |
useEffect(() => { console.log('Executou apenas quando someValue mudar.'); }, [someValue]); |
Sempre inclua todas as variáveis cabíveis dentro do efeito na lista de dependências.
Se o effect não deve ser reexecutado, deixe a lista vazia ([]), o que faz com que ele rode apenas na montagem do componente.
3. Uso incorreto para código assíncrono
Chamadas assíncronas dentro do useEffect exigem cuidado especial.
Um erro frequente é esquecer de tratar cancelamentos ou desmontagem do componente.
Exemplo incorreto:
|
1 2 3 |
useEffect(() => { fetchData().then(data => setData(data)); }, []); |
Se o componente for desmontado antes da resposta chegar, essa linha tentará atualizar o estado de um componente inexistente, possivelmente, gerando memory leaks ou erros de atualização.
Abordagem correta:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
useEffect(() => { let isMounted = true; const fetchDataAsync = async () => { const data = await fetchData(); if (isMounted) { setData(data); } }; fetchDataAsync(); return () => { isMounted = false; // cancela a atualização se desmontar }; }, []); |
4. Efeitos que geram loops de renderização
Outro problema recorrente é usar useEffect para atualizar o estado que, por sua vez, está listado nas dependências do mesmo efeito, criando um loop infinito.
Vacilação:
|
1 2 3 |
useEffect(() => { setCount(count + 1); }, [count]); |
Aqui, toda vez que count muda, o efeito roda de novo e a muda mais uma vez, gerando um ciclo sem fim.
Correção:
|
1 2 3 |
useEffect(() => { setCount(prev => prev + 1); }, []); // roda apenas uma vez |
Sempre e questione: “Este efeito realmente depende dessa variável ou posso calcular o novo valor de outra forma”?
Muitas vezes, o problema é resolvido usando a função de atualização do setState (setCount(prev => …)) sem precisar incluir dependências.
5. Manipulação do DOM
Um dos princípios do React é ser declarativo, ou seja, o React deve ser responsável por atualizar a interface com base no estado.
Usar useEffect para manipular o DOM manualmente fere essa ideia e pode gerar inconsistências.
Incorreto:
|
1 2 3 |
useEffect(() => { document.getElementById('title').style.color = 'red'; }, []); |
Correto:
|
1 2 3 |
const [color, setColor] = useState('red'); return <h1 id="title" style={{ color }}>Título</h1>; |
Ou, se for necessário acessar o DOM (exemplo: integrar uma biblioteca externa), use useRef para manter uma referência controlada pelo React:
|
1 2 3 4 5 6 7 8 9 |
const titleRef = useRef(null); useEffect(() => { if (titleRef.current) { titleRef.current.focus(); // manipulação controlada } }, []); return <h1 ref={titleRef}>Título</h1>; |
Como estamos falando da “maneira React” de fazer as coisas, manipule o DOM apenas quando for inevitável, e sempre através de refs, nunca via document.querySelector().
6. Múltiplos useEffect repetindo lógica
Separar demais a lógica entre vários useEffect pode deixar o código mais difícil de ler e manter, especialmente se eles executam ações relacionadas.
Exemplo equivocado:
|
1 2 3 4 5 6 7 |
useEffect(() => { setUser(loadUser()); }, []); useEffect(() => { setSettings(loadSettings()); }, []); |
Esses dois efeitos estão relacionados ao mesmo contexto (inicialização de dados).
Faz mais sentido unificá-los em um único efeito.
|
1 2 3 4 5 6 7 8 9 |
useEffect(() => { const init = async () => { const user = await loadUser(); const settings = await loadSettings(); setUser(user); setSettings(settings); }; init(); }, []); |
Pense nos efeitos como “reações a eventos externos”.
Se dois efeitos reagem ao mesmo evento (como a montagem do componente), eles podem ser agrupados.
Conclusão
O useEffect é essencial para lidar com efeitos colaterais, mas nem tudo é um efeito colateral.
O useEffect é como o canivete suíço do React: poderoso, mas fácil de usar de forma errada.
A tentação de “colocar tudo no lugar errado” (como na piada do tweet) é grande, mas compreender quando e por que usar o useEffect é o que separa o código confuso do código eficiente.
Ao estar ciente dessas 6 maneiras erradas de usar useEffect de React, junto ao comparativo da maneira adequada de codar em cada uma das situações, você evita bugs, melhora a performance e torna seus componentes muito mais eficientes e assertivos.