Back to blog

Performance em React:Re-renderizações que Todo Desenvolvedor Deveria Conhecer

4 min de leitura
992 palavras

Introdução

Se você já desenvolveu aplicações React de médio a grande porte, provavelmente já enfrentou problemas de performance em algum momento. Talvez você tenha notado que sua aplicação ficou lenta, ou que a experiência do usuário não estava tão fluida quanto deveria ser.

No mundo da otimização de performance em React, existem inúmeras técnicas, bibliotecas e padrões: React.memo, useMemo, useCallback, Signals, React Server Components... a lista é extensa e pode parecer intimidadora. Mas e se eu te dissesse que existe uma técnica simples e eficaz para diagnosticar problemas de performance antes mesmo de pensar em soluções complexas?

Neste artigo, vou compartilhar um método extremamente prático para identificar re-renderizações desnecessárias em seus componentes React - a raiz de muitos problemas de performance.

O Problema das Re-renderizações Silenciosas

No React, quando um componente re-renderiza, isso significa que o React está executando a função daquele componente novamente, comparando o resultado com a versão anterior e atualizando o DOM quando necessário.

Embora o React seja otimizado para fazer isso de forma eficiente, cada re-renderização ainda tem um custo de processamento. O problema surge quando componentes re-renderizam sem necessidade, ou seja, quando não há mudanças reais para refletir na interface.

Isso acontece por diversos motivos:

  • Props sendo recriadas em cada renderização do componente pai
  • Estados sendo atualizados com valores idênticos
  • Falta de memoização em funções ou valores complexos
  • Context Providers que disparam atualizações amplas demais

Mas como identificar esse problema de forma rápida, sem instrumentos complexos ou ferramentas de profiling?

A Técnica dos 10 Segundos

Existe um método impressionantemente simples para diagnosticar re-renderizações desnecessárias. Tudo o que você precisa é adicionar algumas linhas de código ao componente que deseja testar:

const renderCount = useRef(0);
renderCount.current += 1;
console.log("Esse componente re-renderizou", renderCount.current, "vezes");

Adicione esse código dentro do seu componente React, recarregue a página e observe o console. Agora espere por cerca de 10 segundos sem interagir com a aplicação.

Se o número de renderizações continuar subindo sem nenhuma interação do usuário, você tem um problema sério de re-renderizações desnecessárias.

As causas mais comuns de re-renderizações desnecessárias.

1. Props Instáveis

Quando um componente pai cria funções ou objetos diretamente no corpo do componente:

// Problema: novaFuncao é recriada a cada renderização do componente pai
function Pai() {
  const novaFuncao = () => {
    console.log("faz algo");
  };

  return <Filho onAction={novaFuncao} />;
}

2. Estados Atualizados de Forma Incorreta

// Problema: setCount sempre dispara uma re-renderização, mesmo quando o valor é o mesmo
function incrementarDuasVezes() {
  setCount(count + 1);
  setCount(count + 1);// Usa o mesmo valor de 'count', então efetivamente não muda nada
}

3. Context Sem Memoização

// Problema: todo componente que usa UserContext re-renderiza quando qualquer valor muda
const UserContext = React.createContext();

function UserProvider({ children }) {
  const [user, setUser] = useState({});
  const [preferences, setPreferences] = useState({});

// Valor do contexto é recriado a cada renderização
  const value = { user, setUser, preferences, setPreferences };

  return (
    <UserContext.Provider value={value}>
      {children}
    </UserContext.Provider>
  );
}

4. Effects mal configurados

// Problema: efeito sem array de dependências roda a cada renderização
useEffect(() => {
  fetchData();
});// Sem array de dependências!

Como Resolver Re-renderizações Desnecessárias

Uma vez identificado o problema, existem várias técnicas para resolver re-renderizações desnecessárias:

1. Memoização de Componentes

Use React.memo para evitar que componentes re-renderizem quando suas props não mudam:

const ComponenteOtimizado = React.memo(function Componente(props) {
  return <div>{props.texto}</div>;
});

2. Memoização de Valores

Use useMemo para valores calculados e objetos:

const objetoEstavel = useMemo(() => {
  return { complexo: "valor", calculado: expensiveCalculation() };
}, [dependencia1, dependencia2]);

3. Memoização de Funções

Use useCallback para funções:

const handleClick = useCallback(() => {
  console.log("Clicado!", id);
}, [id]);

4. Otimização de Context

Divida seus contextos por domínio e use memoização para valores:

function UserProvider({ children }) {
  const [user, setUser] = useState({});
  const [preferences, setPreferences] = useState({});

  const value = useMemo(() => {
    return { user, setUser, preferences, setPreferences };
  }, [user, preferences]);

  return (
    <UserContext.Provider value={value}>
      {children}
    </UserContext.Provider>
  );
}

5. Correto Uso de useEffect

Sempre forneça um array de dependências adequado:

useEffect(() => {
  fetchData();
}, [id, shouldRefetch]);// Dependências explícitas

Além da Técnica Rápida: Ferramentas Avançadas

Após identificar problemas com nossa técnica simples, você pode querer usar ferramentas mais avançadas:

  1. React DevTools Profiler: Fornece informações detalhadas sobre cada renderização
  2. why-did-you-render: Biblioteca que alerta sobre re-renderizações potencialmente desnecessárias
  3. Lighthouse: Para métricas gerais de performance

Devo Otimizar Tudo?

É importante lembrar que nem toda re-renderização é problemática. O React foi projetado para lidar com renderizações frequentes. A otimização prematura pode tornar seu código mais complexo sem benefícios reais.

Siga estas diretrizes:

  1. Meça antes de otimizar: Use a técnica descrita neste artigo
  2. Priorize componentes pesados: Componentes com cálculos complexos ou que renderizam muitos elementos
  3. Priorize componentes frequentemente atualizados: Componentes que estão no caminho de atualizações frequentes
  4. Teste em dispositivos reais: Especialmente dispositivos móveis de baixo/médio desempenho

Conclusão

Antes de mergulhar em soluções complexas como Signals, Server Components ou até mesmo migrar para frameworks mais novos, entenda e resolva as re-renderizações desnecessárias em sua aplicação React.

A técnica de monitoramento de renderizações com useRef é simples, mas incrivelmente poderosa para diagnosticar problemas. Ela revela a verdade sobre o comportamento interno dos seus componentes e pode guiar otimizações que realmente importam.

Lembre-se do princípio fundamental:

Render é caro. Render sem necessidade é dívida técnica.

E você, já testou seus componentes hoje? Quantas re-renderizações desnecessárias você encontrou?