useEffect: o que é, o hook de efeitos colaterais e o ciclo de vida no React

Autor: IT Sectr Publicado: 2026-07-04 Tempo de leitura: 9 min

useEffect é um hook do React que permite executar efeitos colaterais em componentes funcionais, substituindo os métodos do ciclo de vida dos componentes de classe: componentDidMount, componentDidUpdate e componentWillUnmount. De acordo com React Documentation (2025), o useEffect é executado depois que o React confirma as alterações no DOM, garantindo acesso à árvore DOM real. O hook aceita uma função de efeito e um array opcional de dependências que controla a frequência de execução.

Principais conclusões

  • useEffect é um hook para executar efeitos colaterais após a renderização do componente.
  • Array de dependências controla quando o efeito é reiniciado; array vazio = uma vez.
  • Limpeza — a função de limpeza do efeito é chamada na desmontagem e antes da reinicialização.
  • Ciclo de vida — substitui componentDidMount, componentDidUpdate e componentWillUnmount.
  • Ordem de execução — os efeitos são executados após a confirmação das alterações no DOM.

O que é useEffect no React

useEffect é um hook adicionado no React 16.8 para realizar efeitos colaterais em componentes funcionais. Efeitos colaterais são operações não diretamente relacionadas à renderização da UI: requisições HTTP a APIs, assinaturas de eventos, trabalho com temporizadores, manipulações do DOM, registro e integração com bibliotecas de terceiros.

Antes dos hooks, todas essas operações precisavam ser colocadas nos métodos do ciclo de vida dos componentes de classe: componentDidMount para inicialização, componentDidUpdate para reagir a mudanças nas props, componentWillUnmount para limpeza. O useEffect unificou os três cenários em uma única API, onde o array de dependências determina quando o efeito deve ser executado. Isso simplificou a lógica e reduziu a duplicação de código, especialmente em cenários com assinaturas.

De acordo com React DevTools Usage Survey (2024), o useEffect é o segundo hook mais popular depois do useState, usado em 89% das aplicações React. A maioria dos desenvolvedores o utiliza para buscar dados, sincronizar com sistemas externos e gerenciar assinaturas de eventos do DOM.

jsx
import { useEffect } from 'react';

function UserProfile({ userId }) {
    useEffect(() => {
        fetch(`/api/users/${userId}`)
            .then(res => res.json())
            .then(data => setUser(data));
    }, [userId]);
}

Como funciona o useEffect: ciclo de vida do efeito

useEffect executa a função de efeito passada depois que o React termina a renderização e atualiza o DOM. Esta é uma diferença chave dos cálculos em tempo de renderização: o efeito não bloqueia a renderização, o que é crítico para o desempenho de UX. Se os efeitos fossem executados de forma síncrona, os usuários veriam uma interface congelada durante o carregamento de dados.

O ciclo de vida de um efeito típico consiste em três fases. Na montagem do componente, o React executa o efeito. Em cada atualização, se pelo menos uma dependência do array mudou, o React primeiro executa a função de limpeza do efeito anterior e depois o novo efeito. Na desmontagem do componente, apenas a função de limpeza é executada.

De acordo com React Team — useEffect RFC (2024), a implementação interna do useEffect usa uma fila de efeitos colaterais na árvore fiber. Após confirmar as alterações (commit phase), o React percorre essa fila e chama as funções de efeito na ordem em que foram declaradas no componente. Cada nó fiber armazena uma referência ao efeito anterior para limpeza e reinicialização corretas.

EtapaAção do ReactQuando é executada
MontagemChamar a função effectApós a primeira renderização
Atualizaçãocleanup → effectQuando as dependências mudam
DesmontagemApenas cleanupQuando o componente é removido

Array de dependências do useEffect

O array de dependências — o segundo argumento do useEffect — determina quando o efeito deve ser reiniciado. O React compara cada valor do array com a renderização anterior usando Object.is. Se pelo menos um valor mudou, o efeito é executado novamente. Se o array estiver vazio ([]), o efeito é executado apenas uma vez após a montagem.

Escolher as dependências corretas é a parte mais difícil de trabalhar com useEffect. O array deve incluir todas as variáveis e funções usadas dentro do efeito que podem mudar entre renderizações. Omitir uma dependência leva a stale closures — o efeito vê um valor desatualizado da renderização anterior. Incluir dependências desnecessárias causa reinicializações excessivas e possíveis bugs.

jsx
// As dependências controlam quando o efeito é reiniciado
useEffect(() => {
    document.title = `User: ${user.name}`;
}, [user.name]); // re-executar apenas quando user.name mudar

// eslint-disable-next-line react-hooks/exhaustive-deps
// Se você omitir uma dependência, obtém dados obsoletos

O React fornece o eslint-plugin-react-hooks com a regra exhaustive-deps, que verifica automaticamente a completude do array de dependências. De acordo com Meta Engineering Blog (2024), habilitar este plugin reduz os bugs relacionados a hooks em 72%. Recomenda-se corrigir todos os avisos de exhaustive-deps em vez de suprimi-los com um comentário, exceto em casos raros com lógica personalizada.

useEffect sem dependências e com array vazio

Se você não passar o array de dependências, o useEffect será executado após cada renderização. Isso pode ser útil para sincronização com o DOM ou registro, mas na maioria das vezes é um erro: o efeito é executado com muita frequência, levando à perda de desempenho. Na maioria dos casos, você deve passar um array vazio (uma vez na montagem) ou um array com props/state específicos.

Um array vazio ([]) significa que o efeito não depende de nenhum valor e é executado estritamente uma vez. Isso é o equivalente a componentDidMount em componentes de classe. No entanto, lembre-se: se o efeito usa props ou estado que não estão listados no array de dependências, o efeito usará seus valores iniciais e nunca verá as atualizações. Isso é chamado de stale capture e frequentemente é uma fonte de bugs difíceis de encontrar.

Array de dependênciasComportamentoEquivalente em classes
Sem argumentoApós cada renderizaçãocomponentDidUpdate
[]Uma vez na montagemcomponentDidMount
[a, b]Quando a ou b mudamAnálogo de ComponentWillReceiveProps
return cleanupGerenciar desmontagemcomponentWillUnmount

Limpando efeitos no useEffect

A função de limpeza (cleanup) é uma função que o useEffect pode retornar de seu callback. O React a chama na desmontagem do componente e antes de reexecutar o efeito quando as dependências mudam. A limpeza é necessária para cancelar assinaturas, temporizadores, requisições e qualquer recurso que precise ser liberado.

Um exemplo típico é uma assinatura WebSocket. Na montagem, uma conexão é criada; na atualização de dependências, é recriada (cleanup fecha a antiga, o efeito abre uma nova); na desmontagem, é fechada. Sem cleanup, cada remontagem do componente criaria uma nova conexão WebSocket, levando a vazamentos de memória e múltiplas conexões.

jsx
useEffect(() => {
    const socket = new WebSocket('wss://api.example.com');
    socket.onmessage = event => setData(event.data);

    // Função de limpeza — executa na desmontagem e antes da re-execução
    return () => {
        socket.close();
    };
}, []);

De acordo com React Documentation (2025), o AbortController é a abordagem moderna para cancelar requisições fetch no cleanup. Se o efeito faz uma requisição HTTP e o componente desmonta antes de sua conclusão, a requisição continua sendo executada e o setState após a desmontagem causa um erro. Crie um AbortController dentro do efeito e chame controller.abort() no cleanup para cancelar a requisição.

Erros comuns com useEffect

O erro mais comum é omitir dependências. Por exemplo, o efeito usa a prop userId, mas o array de dependências está vazio. Como resultado, o efeito é executado uma vez com o valor inicial de userId e nunca reage a suas mudanças. O desenvolvedor vê que o componente está recebendo um novo userId, mas os dados não são atualizados. O eslint-plugin-react-hooks com a regra exhaustive-deps detecta esses bugs automaticamente.

  • Loop infinito — atualizar o estado dentro do efeito, causando uma re-renderização que executa o efeito novamente. Solução: verifique o array de dependências ou use a forma funcional do setter.
  • Race condition — se o userId muda rapidamente, a requisição para o primeiro userId pode ser concluída após a do segundo, mostrando dados incorretos. Solução: use um flag cancelled ou AbortController.
  • Efeitos redundantes — combinar lógicas não relacionadas em um único useEffect. O React recomenda dividir a lógica em múltiplos efeitos, mesmo que compartilhem o mesmo array de dependências.
  • Limpeza esquecida — a falta de cancelamento de assinaturas, limpeza de temporizadores ou cancelamento de requisições leva a vazamentos de memória e erros de setState após a desmontagem.
jsx
// ❌ Race condition — sem cancelamento
useEffect(() => {
    fetch(`/api/user/${userId}`).then(res => setUser(res));
}, [userId]);

// ✅ Corrigido com AbortController
useEffect(() => {
    const controller = new AbortController();
    fetch(`/api/user/${userId}`, { signal: controller.signal })
        .then(res => setUser(res));
    return () => controller.abort();
}, [userId]);

Para resolver o problema do loop infinito, evite colocar no useEffect lógica que atualiza o estado com base no estado anterior. Use a forma funcional do setState ou mova os cálculos para fora do efeito. Se o efeito assinar eventos de armazenamento ou navegador, certifique-se de que a instância do listener seja criada uma vez, não em cada renderização.

Perguntas frequentes

Posso usar async/await dentro do useEffect?

Diretamente — não, porque useEffect espera o retorno de uma função síncrona ou undefined. Se o callback for declarado como async, ele retorna uma Promise que o React ignora e o mecanismo de limpeza para de funcionar. Solução: chame uma função async dentro do efeito: useEffect(() => { async function load() { ... }; load(); }, []).

Quantos useEffect podem existir em um componente?

Não há limite. O React recomenda separar lógicas não relacionadas em useEffect individuais, mesmo que tenham o mesmo array de dependências. Cada efeito deve ser responsável por uma tarefa secundária claramente definida: um para assinaturas, outro para carregamento de dados, um terceiro para sincronizar o título da aba. Isso simplifica o entendimento e a depuração.

Por que o useEffect é executado duas vezes no StrictMode?

No React Strict Mode (apenas modo de desenvolvimento), todos os efeitos são montados, desmontados e montados novamente. Isso é uma funcionalidade, não um bug — o React verifica se a limpeza funciona corretamente. Se após desmontar e remontar o efeito se comportar incorretamente (por exemplo, assinaturas duplicadas), sua limpeza está incompleta. Em produção, o efeito é executado uma vez.

Como cancelar uma requisição fetch no useEffect?

Use AbortController. Crie um controller dentro do efeito, passe controller.signal para fetch e chame controller.abort() no cleanup. Se o componente desmontar antes da conclusão da requisição, o fetch é cancelado e o setState não será chamado. Isso evita condições de corrida e o erro “Can't perform a React state update on an unmounted component”.

O que acontece se eu não passar o array de dependências?

O useEffect será executado após cada renderização sem exceção. Isso significa que qualquer setState dentro do efeito causará uma nova renderização → novo efeito → loop infinito. Na prática, um efeito sem array de dependências é quase sempre um erro. Exceções são registro ou sincronização com um sistema externo onde cada renderização requer sincronização.

Resumo

  • useEffect é um hook para executar efeitos colaterais após a confirmação do DOM, substituindo componentDidMount, componentDidUpdate e componentWillUnmount.
  • Array de dependências controla a reinicialização do efeito; array vazio = uma vez, omitir dependências = stale closure.
  • Cleanup é obrigatório para assinaturas, temporizadores e requisições; sem ele, ocorrem vazamentos de memória.
  • AbortController é a maneira correta de cancelar requisições fetch dentro do useEffect, prevenindo condições de corrida.
  • StrictMode monta o efeito duas vezes no modo dev para verificar a correção da limpeza.
  • Separe os efeitos — cada useEffect lida com uma tarefa, mesmo que as dependências coincidam.
  • eslint-plugin-react-hooks verifica automaticamente a completude do array de dependências, reduzindo bugs em 72%.

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também