Destino Dev

Blog

useEffect vs useLayoutEffect no React: quando usar, exemplos e armadilhas

Por John Pires · 13 de março de 2026

Contexto: quando você vai usar isso

Por que existem efeitos em componentes React

Hooks como useEffect e useLayoutEffect servem para executar código que precisa rodar depois que o React atualiza o DOM. Eles resolvem tarefas que não são puras: ler ou escrever no DOM, iniciar timers, configurar assinaturas (sockets, ouvintes), ou sincronizar valores com APIs externas. Em componentes funcionais, não há métodos de ciclo de vida tradicionais; os effects são a forma padrão de reagir a mudanças.

Para quem está começando: pense no render como a construção de um esqueleto virtual. Os effects são os passos que você faz depois da montagem para ajustar detalhes reais no navegador.

Diferença principal entre useEffect e useLayoutEffect

Resumo prático: useEffect roda após o paint no navegador (assíncrono) e não bloqueia a renderização. useLayoutEffect roda de forma síncrona depois que o DOM foi atualizado mas antes do browser pintar — ele bloqueia até terminar. Isso faz do useLayoutEffect a ferramenta certa para leituras de layout e ajustamentos que, se feitos assíncronamente, causariam um deslocamento visível (layout shift).

Importante para iniciantes: prefira useEffect na maioria dos casos. Use useLayoutEffect somente quando você precisa medir o DOM e aplicar mudanças antes do paint.

Explicação visual com diagramas mentais

Linha do tempo: render -> commit -> paint

Imagine o ciclo do React assim:

// Simplificação do ciclo de vida (timeline)
1. Render (virtual DOM atualizado)      // função componente executa
2. Commit (aplica mudanças no DOM real) // DOM real recebe as mutações
3. useLayoutEffect (síncrono)           // roda antes do paint
4. Paint (navegador desenha pixels)
5. useEffect (assíncrono)                // roda depois do paint

Se você precisa ler dimensões do DOM e ajustar CSS antes do paint, escolha useLayoutEffect. Caso contrário, use useEffect para não bloquear a renderização.

Analogia: montagem de móveis

Pense numa montagem de móveis: fazer o frame é o render. Colocar os parafusos críticos antes de colocar o acabamento é o useLayoutEffect (sincrono). Passar a lixa e o verniz depois que o móvel já está no lugar é o useEffect (assíncrono). Se você lixar depois do verniz, estraga; se você faz tudo no caminho crítico, atrasa a entrega.

Regra prática: bloquear o fluxo grande demais atrasa a interface. Use o sincronismo só quando necessário.

Primeiro exemplo simples

Contador que atualiza o título da página

Contexto: um contador que atualiza o document.title. Isso não precisa bloquear o paint, portanto useEffect é apropriado. O exemplo abaixo mostra o comportamento correto e uma versão com useLayoutEffect que funciona, mas é desnecessária aqui.

// O que faz: atualiza document.title com o contador
import React, { useState, useEffect, useLayoutEffect } from 'react';

export function CounterUseEffect() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Contador: ${count}`;
  }, [count]);

  return (
    Contador: {count} 
      setCount(c => c + 1)}>+
  );
}

O que acontece: o navegador atualiza a UI primeiro; depois o effect roda e altera o título da aba. O usuário não nota nenhuma lentidão porque a atualização do title é rápida e fora do caminho crítico do paint.

Por que não usar useLayoutEffect aqui

Se você trocar por useLayoutEffect o código funciona do mesmo jeito funcionalmente, mas passa a bloquear o paint até que o effect termine. Em cenários simples isso não é perceptível, mas em componentes com muitos cálculos ou em dispositivos lentos, você cria potenciais travamentos.

// Versão desnecessária: bloqueante para uma tarefa não crítica
import React, { useState, useLayoutEffect } from 'react';

export function CounterUseLayoutEffect() {
  const [count, setCount] = useState(0);

  useLayoutEffect(() => {
    document.title = `Contador: ${count}`;
  }, [count]);

  return (
    Contador: {count} setCount(c => c + 1)}>+
  );
}


O que acontece: comportamentalmente idêntico, porém você sacrifica performance sem necessidade. Portanto: use useEffect por padrão.

Segundo exemplo mais realista

Medindo um elemento e evitando layout shift

Contexto: você precisa medir a largura de uma caixa logo após ela ser renderizada e, com base nisso, aplicar uma correção de estilo que evita um 'salto' visual. Aqui useLayoutEffect é a escolha correta porque a leitura e escrita do DOM precisam ocorrer antes do paint.

// O que faz: mede a largura de um elemento e aplica uma classe de ajuste
import React, { useRef, useState, useLayoutEffect } from 'react';

export function MeasuredBox() {
  const ref = useRef(null);
  const [wide, setWide] = useState(false);

  useLayoutEffect(() => {
    if (!ref.current) return;
    const w = ref.current.getBoundingClientRect().width;
    // Se for maior que 300px, ativa um ajuste (evita reflow visível)
    setWide(w > 300);
  }, []);

  return (
      Caixa medida — largura atual aplicada antes do paint
  );
}


O que acontece: ao renderizar, o React aplica o DOM, entra no commit e executa o useLayoutEffect. O efeito mede a largura e ajusta o estado antes do browser pintar. Assim, a cor/estilo final é aplicado sem que o usuário veja um estado intermediário.

Versão com useEffect (o problema)

Se a mesma lógica usar useEffect, o paint acontece primeiro com o estado inicial. Só depois a medida é feita e o estado atualiza, causando um re-paint perceptível — o famoso layout shift.

// O que faz: mesma medição, porém usando useEffect -> causa layout shift
import React, { useRef, useState, useEffect } from 'react';

export function MeasuredBoxWithShift() {
  const ref = useRef(null);
  const [wide, setWide] = useState(false);

  useEffect(() => {
    if (!ref.current) return;
    const w = ref.current.getBoundingClientRect().width;
    setWide(w > 300);
  }, []);

  return (
    
      Caixa medida — pode causar layout shift
    
  );
}

O que acontece: visualmente, o usuário pode ver a caixa em uma cor (estado inicial) e, em seguida, o repaint altera a cor. Em UIs sensíveis a estabilidade visual, esse comportamento é indesejável.

Erros comuns de iniciante e como evitar

Esquecer dependências ou listá-las incorretamente

Muitos iniciantes omitem dependências do array do effect ou colocam funções geradas inline, causando efeitos que não disparam quando deveriam ou disparos infinitos. Regras práticas:

  • Inclua tudo que você usa dentro do effect (props, state, refs, funções externas).

  • Se a função muda a cada render, use useCallback ou mova a lógica para dentro do effect.

  • Evite desabilitar o lint (eslint-plugin-react-hooks) sem entender o risco.

Exemplo de erro comum: criar uma função dentro do componente e colocá-la no array de dependências sem memoizar — isso aciona o effect toda render.

// O que faz: exemplo com loop infinito
import React, { useState, useLayoutEffect } from 'react';

export function BadLoop() {
  const [count, setCount] = useState(0);

  const doSomething = () => {
    // função recriada a cada render
    return count + 1;
  };

  useLayoutEffect(() => {
    // depende de doSomething; como ela muda a cada render, o effect repete
    const next = doSomething();
    setCount(next);
  }, [doSomething]);

  return {count};
}

Correção: remova a função das dependências ou memoize-a com useCallback, e prefira usar o padrão do setState com função quando possível.

Bloquear o thread com lógica pesada em useLayoutEffect

Recurso perigoso: colocar cálculos pesados dentro de useLayoutEffect. Como ele é síncrono no commit, tudo que rodar ali atrasa o paint e deixa a UI travada. Se precisar computar muito, mova para Web Worker, memoize, ou use useEffect com técnicas para reduzir flashes visuais.

Lista de armadilhas:

  • Executar loops demorados em useLayoutEffect.

  • Fazer chamadas de rede longas no useLayoutEffect (useEffect é melhor).

  • Misturar manipulação direta do DOM e atualizações de estado sem checar se o componente ainda está montado.

Prática segura: se precisar medir e também buscar dados, meça com useLayoutEffect e faça a fetch com useEffect. Separe responsabilidades.

Conclusão prática

Regras rápidas para escolher

  1. Por padrão, use useEffect.

  2. Se você precisa ler layout (getBoundingClientRect) e mudar o DOM ou estado para evitar um layout shift, use useLayoutEffect.

  3. Evite lógicas pesadas em useLayoutEffect; prefira otimizar o algoritmo ou usar workers.

  4. Mantenha o array de dependências correto para evitar efeitos incorretos ou loops.

Checklist antes de escolher

Antes de trocar para useLayoutEffect, responda:

  • Preciso medir o DOM e aplicar a mudança antes do paint?

  • A mudança evita um salto visual perceptível?

  • O trabalho no effect é rápido e seguro de rodar síncronamente?

Se a resposta for sim às três, use useLayoutEffect. Caso contrário, use useEffect.

Dica final: começar por useEffect e só migrar para useLayoutEffect quando você tiver um problema claro de layout é uma abordagem pragmática e segura.

Com esses exemplos e regras você tem uma base prática para decidir entre os dois hooks. Experimente os exemplos no seu ambiente, observe o comportamento visual e use ferramentas como o profiler do React para medir impacto de performance.

Boa prática: sempre documente por que um effect é useLayoutEffect no código — isso ajuda futuros mantenedores a entenderem a decisão.

Assuntos: React, hooks, uselayouteffect, useeffect, Performance

Leia também