`useEffect` não é sua primeira opção. É seu último recurso.
Todo dev React aprende useEffect cedo. E passa anos usando errado.
Não é culpa sua. O nome parece genérico o suficiente para encaixar em qualquer coisa. Mas a API tem um contrato específico: sincronizar com o mundo externo ao React. DOM, network, browser API.
Se você não está cruzando essa fronteira, você provavelmente não precisa de efeito.
Estado derivado não precisa de efeito
O erro mais comum. Um valor depende de outro estado, então você os sincroniza:
// Don't do thisconst [items, setItems] = useState([]);const [count, setCount] = useState(0); useEffect(() => { setCount(items.length);}, [items]);Duas renderizações para algo que você pode calcular diretamente. React renderiza com count desatualizado, o efeito roda, atualiza, React renderiza de novo.
Simplesmente calcule:
// Do this insteadconst [items, setItems] = useState([]);const count = items.length;Sem estado. Sem efeito. Sem render extra.
Filtrar dados não precisa de efeito
Mesmo erro, formato diferente:
// Don't do thisconst [query, setQuery] = useState("");const [filtered, setFiltered] = useState(todos); useEffect(() => { setFiltered(todos.filter((t) => t.text.includes(query)));}, [todos, query]);Duas renderizações por mudança. Calcule inline:
// Do this insteadconst [query, setQuery] = useState("");const filtered = todos.filter((t) => t.text.includes(query));Se o cálculo for pesado, use useMemo. Ele faz cache do resultado, não agenda atualização de estado como useEffect + setState:
const filtered = useMemo( () => todos.filter((t) => t.text.includes(query)), [todos, query]);Responder a eventos não precisa de efeito
Usuário fez algo? Responda no handler:
// Don't do thisconst [submitted, setSubmitted] = useState(false); useEffect(() => { if (submitted) { sendAnalytics("form_submit"); setSubmitted(false); }}, [submitted]); function handleSubmit() { setSubmitted(true);}Isso é uma forma indireta de dizer "quando o usuário enviar, dispare o analytics". Coloque direto no handler:
// Do this insteadfunction handleSubmit() { sendAnalytics("form_submit");}A regra é simples: ação do usuário -> handler. Componente apareceu na tela e precisa sincronizar com algo externo -> efeito.
Resetar estado ao mudar prop não precisa de efeito
// Don't do thisfunction ProfilePage({ userId }) { const [comment, setComment] = useState(""); useEffect(() => { setComment(""); }, [userId]); // ...}Renderiza com estado desatualizado primeiro, depois o efeito limpa. Use key:
// Do this instead// In the parent:<ProfilePage userId={id} key={id} />key diferente = nova instância. Estado começa limpo, sem código extra.
Quando useEffect é a resposta certa
- Event listeners (
resize,scroll, WebSockets) - Integrações com bibliotecas que manipulam o DOM diretamente
- Fetch de dados, mas prefira SWR ou TanStack Query, eles resolvem race conditions que você vai ignorar
// This is a valid use of useEffectuseEffect(() => { const handler = () => setWindowWidth(window.innerWidth); window.addEventListener("resize", handler); return () => window.removeEventListener("resize", handler);}, []);A pergunta que resolve tudo
Antes de escrever useEffect:
Estou sincronizando com algo fora do React?
- Calculando um valor a partir de estado ou props? Calcule durante o render.
- Cálculo pesado? Use
useMemo. - Respondendo a ação do usuário? Use um handler.
- Precisa resetar estado quando prop muda? Use
key.
useEffect é válvula de escape. Não é padrão.