Menu

Strict Mode no React: por que efeitos executam duas vezes

StrictMode é um wrapper só de desenvolvimento que renderiza os componentes duas vezes, executa setup, limpeza e setup dos efeitos na montagem e avisa sobre APIs descontinuadas, para que renderizações impuras e limpezas faltando apareçam cedo. Em produção ele não faz nada.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

<StrictMode> é um componente do React que liga verificações extras para tudo dentro dele, só em desenvolvimento. Ele renderiza cada componente duas vezes, executa o setup, a limpeza e o setup de novo de cada efeito na montagem, executa as ref callbacks duas vezes e avisa sobre APIs descontinuadas. É por isso que o useEffect "executa duas vezes" em desenvolvimento. Em um build de produção ele não faz nada.

Você o liga envolvendo o seu app, normalmente em main.jsx:

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';

createRoot(document.getElementById('root')).render(
    <StrictMode>
        <App />
    </StrictMode>,
);

As prévias desta página rodam como um build de produção, então o StrictMode não teria efeito visível nelas. Em vez disso, cada exemplo abaixo tem o tipo de bug que o StrictMode existe para pegar e permite que você o provoque à mão. Aqui um efeito adiciona um listener de clique em document e nunca o remove. Esconda e mostre o painel algumas vezes e depois clique na página.

Cada Show adiciona mais um listener, e nenhum é removido, então um único clique registra uma vez por montagem. Esse é um vazamento que um usuário poderia causar abrindo e fechando um painel. O StrictMode o mostraria logo na primeira montagem: em desenvolvimento ele monta, desmonta e monta de novo, então você veria duas linhas de log por clique antes mesmo de tocar no botão.

Efeitos: setup, limpeza, setup

Na montagem, o StrictMode executa o seu efeito, depois a limpeza dele e depois o efeito de novo. Ele está simulando o que acontece quando um usuário sai de uma tela e volta, e conferindo se a sua limpeza desfaz o seu setup. Se desfaz, o segundo setup deixa tudo exatamente como um setup só deixaria, e você não vê nada de errado. Se não desfaz, o bug aparece na hora.

A correção do logger é uma limpeza que remove a mesma função que adicionou:

Alterne quantas vezes quiser: cada clique registra uma linha enquanto o logger está visível. Com StrictMode em desenvolvimento, o console começaria com setup, cleanup, setup, e o app continuaria se comportando corretamente. Essa sequência é o sinal de um efeito saudável, não um problema para desligar.

Efeitos comuns que precisam de limpeza: event listeners, setInterval e setTimeout, assinaturas, conexões de WebSocket ou chat e requisições cuja resposta deve ser ignorada se o componente não existir mais. A página do useEffect mostra o padrão de limpeza para cada um.

Renderizações: chamadas duas vezes

O StrictMode também chama a função do seu componente duas vezes a cada renderização (e as funções que você passa para useState, useMemo e useReducer, e o constructor, o render e o shouldComponentUpdate de classes). O React fica com o resultado de uma chamada e descarta o outro. Um componente puro retorna o mesmo JSX nas duas vezes, então nada muda.

Um componente que muda algo fora de si enquanto renderiza não é puro, e chamá-lo duas vezes faz a mudança acontecer duas vezes. Este adiciona uma entrada "rendered at" a um array que recebe como prop:

Cada nova renderização adiciona uma entrada que ninguém pediu. Aqui você só percebe depois de clicar. Com StrictMode, a primeira renderização já mostraria duas entradas novas em vez de uma. A correção é calcular durante a renderização sem mutar: const shown = [...entries, 'extra'].

O React 19 acrescenta um detalhe: durante a renderização dupla, useMemo e useCallback reutilizam o resultado da primeira chamada em vez de calculá-lo duas vezes.

Ref callbacks

Desde o React 19, o StrictMode também executa as ref callbacks duas vezes na montagem: conectar, limpar, conectar. Uma ref callback que adiciona um listener ou registra o nó em algum lugar precisa retornar uma limpeza, exatamente como um efeito:

<div
    ref={(node) => {
        const observer = new ResizeObserver(onResize);
        observer.observe(node);
        return () => observer.disconnect();
    }}
/>

Avisos de APIs descontinuadas

O StrictMode avisa sobre APIs que estão saindo de cena, principalmente em componentes de classe, como os antigos ciclos de vida UNSAFE_componentWillMount, UNSAFE_componentWillReceiveProps e UNSAFE_componentWillUpdate. Algumas APIs sobre as quais ele avisava, como string refs e findDOMNode, foram removidas por completo do React 19.

Ligando em parte do app

<StrictMode> é um componente normal, então você pode envolver só parte da árvore. Isso ajuda ao adotá-lo em um app grande: comece pelas telas novas e vá ampliando.

function App() {
    return (
        <>
            <Header />
            <StrictMode>
                <Sidebar />
                <Content />
            </StrictMode>
            <Footer />
        </>
    );
}

Sidebar e Content (e tudo o que eles renderizam) recebem as verificações; Header e Footer não. Não há como desligá-lo para uma subárvore dentro de uma parte estrita.

Onde ele vem ligado por padrão:

  • Vite: o template React envolve <App /> em <StrictMode> no main.jsx.
  • Next.js: o App Router o liga por padrão; reactStrictMode em next.config.js o controla.

Em produção ele não faz nada

Um build de produção ignora <StrictMode>: uma renderização, uma execução de efeito, nenhum aviso. Então as chamadas duplas nunca custam nada aos seus usuários, e não há motivo para remover o wrapper antes de publicar.

Se um efeito duplo quebra algo em desenvolvimento, como uma requisição enviada duas vezes ou um evento de analytics registrado duas vezes, isso é informação. Na maioria das vezes a correção é uma limpeza (um indicador ignore para uma requisição, um disconnect para uma conexão). Para algo que realmente precisa acontecer uma vez por carregamento do app, como inicializar um SDK, execute-o fora de qualquer componente, no nível do módulo, ou proteja-o com um indicador no nível do módulo.

let didInit = false;

function App() {
    useEffect(() => {
        if (didInit) return;
        didInit = true;
        initAnalytics(); // runs once per page load, even in StrictMode
    }, []);
}

Evite o contorno comum de uma ref que pula a segunda execução do efeito. Ele esconde o aviso sem corrigir a limpeza faltando, e o bug volta na primeira vez em que o componente realmente desmonta e monta de novo, como faz o logger acima.

Perguntas frequentes

O que o StrictMode do React faz?

Só em desenvolvimento, ele renderiza duas vezes cada componente dentro dele, executa o setup, a limpeza e o setup de novo de cada efeito quando o componente monta, executa as ref callbacks duas vezes do mesmo jeito e avisa sobre APIs descontinuadas. As chamadas extras expõem renderizações que não são puras e efeitos sem limpeza.

Por que meu useEffect executa duas vezes?

Porque o componente está dentro de <StrictMode> e você está rodando um build de desenvolvimento. O React o monta, simula uma desmontagem (sua limpeza executa) e o monta de novo. Se o segundo setup causa um bug, falta uma limpeza no efeito.

O StrictMode afeta a produção?

Não. Um build de produção renderiza uma vez e executa cada efeito uma vez, com ou sem <StrictMode>. Você pode deixá-lo no código.

Como desativo o StrictMode?

Remova o wrapper <StrictMode>, normalmente em main.jsx ou index.js, ou defina reactStrictMode: false em next.config.js. Corrigir a limpeza costuma ser melhor: a execução dupla está apontando para um bug que usuários reais podem encontrar.

Por que o console.log é impresso duas vezes?

O StrictMode chama a função do seu componente duas vezes por renderização em desenvolvimento. O React DevTools pode deixar acinzentados os logs da segunda chamada, ou escondê-los, nas configurações.

Ilustração das linguagens de programação do Coddy

Aprenda a programar com o Coddy

COMEÇAR