Marcio Cunha

React travando? Como evitar re-render desnecessário

Descubra como os ganchos de memorização no React congelam instâncias e evitam gargalos visuais em aplicações web modernas.

Marcio Cunha•5 min
Também disponível em:EnglishEspañol
Resumo
  • O re-render excessivo ocorre quando o React redesenha componentes na tela sem que os dados reais tenham mudado de fato.
  • O gancho useMemo guarda o resultado de cálculos pesados para evitar que eles rodem a cada clique ou mudança de estado.
  • O gancho useCallback congela funções para que referências na memória não mudem à toa e disparem atualizações falsas.
  • A memorização exige cuidado pois guardar tudo na memória pode gastar mais recursos do que recalcular dados simples.
  • Organizar bem o código e evitar o excesso de propriedades passadas entre componentes ajuda a manter a interface rápida e fluida.

Por que o React redesenha a tela tantas vezes?

Quando desenvolvemos interfaces web modernas, o React funciona como um pintor rápido que redesenha a tela sempre que algo muda. No entanto, esse pintor muitas vezes exagera e pinta cômodos inteiros da casa só porque um único interruptor foi acionado. Na prática, isso significa que um componente simples na ponta da página acaba rodando seu código de novo, mesmo quando os dados que ele exibe continuam exatamente iguais. Esse comportamento padrão garante que a tela nunca fique desatualizada, mas gera um consumo invisível de processamento que afeta a fluidez da aplicação.

Para entender o tamanho do problema, imagine uma lista com mil itens onde cada linha verifica se foi alterada. Se o usuário digita uma letra em um campo de busca no topo da página, o React pode decidir reavaliar cada um dos mil itens, travando o navegador por alguns milissegundos. Esse atraso perceptível na digitação arruína a experiência do usuário. Para dominar esse comportamento, é fundamental compreender como o motor do React decide quando atualizar a tela e como o fluxo de dados se relaciona com a estrutura de como evitar props drilling na componentizacao frontend, reduzindo o esforço desnecessário em cascata.

O papel dos ganchos de memorização na performance

Para colocar freios nesse comportamento exagerado, o React oferece ferramentas conhecidas como ganchos de memorização, sendo o useMemo e o useCallback os principais guardiões da performance. A memorização funciona como uma caixa de lembretes no cérebro: em vez de fazer uma conta complicada ou criar uma nova ferramenta do zero a cada segundo, o sistema guarda o resultado anterior e só refaz o trabalho se os ingredientes principais mudarem de verdade. Isso evita que objetos e funções sejam recriados na memória a cada segundo.

Na prática, sempre que o React roda um componente, ele joga fora todas as funções e variáveis criadas nele e cria novas instâncias do zero. Para o computador, criar coisas novas o tempo todo consome energia e obriga o sistema de limpeza de memória a trabalhar dobrado. Quando congelamos essas instâncias com os ganchos corretos, dizemos ao JavaScript para manter a mesma referência na memória, o que impede que componentes filhos pensem que receberam dados novos e precisem se redesenhar à toa.

Como usar o useMemo para congelar cálculos pesados

O gancho useMemo é ideal para guardar o resultado de operações matemáticas demoradas ou filtragens complexas em grandes listas. Pense nele como um cofre: você coloca um valor lá dentro e diz ao React que só vale a pena abrir o cofre se a chave principal sofrer alguma alteração. Se o usuário clicar em um botão irrelevante que mexe em outra parte da tela, o React olha para o cofre, vê que a chave não mudou e entrega o valor guardado instantaneamente, sem recalcular nada.

Veja um exemplo prático de como aplicar esse conceito em um componente que filtra dados de forma intensiva:

import React, { useState, useMemo } from 'react';

function ListaProdutos({ produtos, termoBusca }) {
  const produtosFiltrados = useMemo(() => {
    console.log('Filtrando produtos...');
    return produtos.filter(p => p.nome.toLowerCase().includes(termoBusca.toLowerCase()));
  }, [produtos, termoBusca]);

  return (
    <ul>
      {produtosFiltrados.map(p => (
        <li key={p.id}>{p.nome}</li>
      ))}
    </ul>
  );
}

Nesse código, a mensagem no console só aparece quando a lista de produtos ou o termo digitado realmente mudam. Se o usuário interagir com outro elemento da página que altere um estado vizinho, a filtragem pesada é ignorada com sucesso, poupando ciclos preciosos do processador e mantendo a interface ágil e responsiva para quem está navegando.

Como o useCallback evita que funções recriadas disparem re-renders

Enquanto o useMemo guarda valores e resultados de contas, o useCallback tem a missão específica de congelar funções. No JavaScript, duas funções com exatamente o mesmo código escrito dentro delas nunca são consideradas iguais se ocupam lugares diferentes na memória. Quando passamos uma função como propriedade para um componente filho, o React acha que aquela função é totalmente nova a cada atualização do pai, o que força o filho a se redesenhar por precaução.

O uso desse gancho é indispensável quando enviamos funções de clique ou envio de formulário para componentes filhos que são muito pesados ou que usam a ferramenta de comparação de mudanças. Ao envolver a função com o useCallback, garantimos que o endereço dela na memória permaneça idêntico enquanto as dependências principais não forem alteradas. Assim, evitamos chamadas em cascata desnecessárias que afetam o desempenho geral do sistema.

Quando a otimização se torna um problema

Um erro comum entre desenvolvedores iniciantes e até experientes é achar que devem colocar ganchos de memorização em absolutamente tudo no código. A memorização não é de graça: para guardar valores na memória, o React gasta espaço e precisa fazer verificações adicionais a cada ciclo para saber se as dependências mudaram ou não. Se você memoriza cálculos simples ou funções que mudam o tempo todo, o custo de gerenciar essa memória extra acaba sendo maior do que simplesmente recalcular o dado na hora.

Na engenharia de software, cada decisão arquitetural exige avaliar os trade-offs envolvidos, ponderando ganhos e perdas operacionais. O segredo para uma aplicação rápida não é congelar cada linha de código, mas sim medir onde estão os gargalos reais usando ferramentas de diagnóstico e aplicar a otimização apenas onde o custo de recálculo realmente prejudica a experiência de quem usa o sistema, mantendo o código limpo e legível. Para complementar essa implementação em produção, vale conferir também nossas análises técnicas sobre Como testar fusíveis térmicos abertos com multímetro e Traceroute: como rastrear saltos de rede e encontrar gargalos.

Considerações finais sobre performance no React

Dominar o fluxo de renderização e o uso correto dos ganchos de memorização transforma a maneira como construímos aplicações web. Compreender que cada componente é um reflexo do estado atual ajuda a desenhar arquiteturas mais limpas, onde os dados fluem de forma previsível e controlada. Lembre-se sempre de que a melhor otimização é aquela fundamentada em dados reais de uso e medições precisas, garantindo que o esforço de engenharia traga frutos visíveis para a performance da aplicação.