

Junior Alves
Senior Developer
Foto: Unsplash
Atualizado: 28 de julho de 2025 às 05:49
Leitura: 6 minutos de leitura
Criado: 28 de julho de 2025
O Performance Killer Silencioso no seu Código Javascript
Por que a Referential Equality está custando a performance da sua aplicação React.js?
Introdução
Você já usou o Profiler do React e se perguntou "Por que car@lh# este componente está renderizando de novo?!". Muitas vezes, a causa não é uma mudança de estado óbvia, mas sim um conceito fundamental do Javascript que age nas sombras: a Igualdade Referencial.
Nesse artigo, vamos entender a fundo esse conceito, como ele silenciosamente degrada a performance da sua aplicação e aprender a usar as ferramentas que o React nos dá para domar esse conceito.
O Problema: A Igualdade Referencial
Antes de qualquer solução, precisamos entender claramente o problema. Em Javascript, a forma como a igualdade (===
) é verificada difere drasticamente entre tipos de dados.
- Tipos Primitivos (string, number, boolean): São comparados por valor.
10 === 10
étrue
. Simples. - Tipos de Objeto (object, array, function): São comparados por referência, ou seja, pelo seu endereço na memória.
Isso significa que dois objetos com conteúdo idêntico não são iguais se não forem o mesmo objeto na memória.
// Mesmo que o conteúdo seja idêntico...
const obj1 = { id: 1 };
const obj2 = { id: 1 };
// ...eles não são o mesmo objeto na memória.
console.log(obj1 === obj2); // false
E por que isso é um "assassino de performance" em React?
Um componente React é uma função que executa novamente a cada renderização. Qualquer objeto, array ou função declarada dentro dele é recriada do zero, recebendo um novo endereço de memória a cada vez.
Quando o React vai renderizar um componente filho, ele verifica se as props mudaram usando a comparação ===
. Para o React, uma nova referência significa uma nova prop, o que aciona uma nova renderização, mesmo que o conteúdo da prop seja visualmente o mesmo. Esse é o ciclo de renderizações desnecessárias que deixa seu app lento.
Entender o "porquê" disso é crucial, pois sem ele, você estará constantemente lutando contra os mecanismos de otimização do React em vez de usá-los a seu favor.
A Solução: Memoization
É aqui que entra a memoization. Essa é uma técnica de otimização que permite que o React "lembre" de coisas (o resultado de um cálculo, uma função, ou um componente renderizado) e pule o trabalho de recriá-las se os inputs não tiverem mudado.
Eu pessoalmente já vi como a aplicação estratégica de memoization pode transformar um dashboard lento e terrível de mexer em uma experiência de usuário fluida e rápida. Bora ver como.
Ferramentas de Memoization do React
O React nos oferece três ferramentas principais para domar a igualdade referencial. Cada uma tem um propósito específico.
1. React.memo()
: Memoizando Componentes
É um HOC (Higher-Order Component) que envolve um componente. Ele impede a re-renderização desse componente se suas props não tiverem sofrido alteração de referência. É a sua primeira linha de defesa.
2. useMemo()
: Memoizando Valores
Este hook memoiza um valor, como um objeto ou o resultado de um cálculo caro. Você o usa para duas coisas principais:
- Evitar re-executar um cálculo pesado a cada render.
- Garantir a mesma referência de um objeto ou array que será passado como prop para um componente filho memoizado com
React.memo
.
3. useCallback()
: Memoizando Funções
Similar ao useMemo
, mas específico para funções. Seu propósito é garantir que a referência de uma função permaneça estável entre as renderizações. Seus principais usos são:
- Passar uma função como prop para um componente filho memoizado com
React.memo
. - Servir como uma dependência estável para outros hooks, como
useEffect
, evitando loops infinitos.
Aplicação Prática: Antes e Depois
Vamos ver um exemplo prático. Considere este componente UserProfile
que renderiza um Dashboard
e um Button
.
Antes (O Problema)
// Componentes simples (sem memoization)
const Dashboard = ({ options }: { options: { color: string } }) => {
console.log('Dashboard re-renderizou!');
return <div style={{ color: options.color }}>Dashboard</div>;
};
const Button = ({
children,
...rest
}: ButtonHTMLAttributes<HTMLButtonElement>) => {
console.log('Button: re-render');
return <button {...rest}>{children}</button>;
};
// Componente Pai
const UserProfile = () => {
// A cada render, 'options' e 'handleSave' recebem uma nova referência.
const options = { color: 'blue' };
const handleSave = () => console.log('salvando...');
return (
<>
<Dashboard options={options} />
<Button onClick={handleSave}>Salvar</Button>
</>
);
};
Neste cenário, qualquer renderização de UserProfile
fará com que Dashboard
e Button
re-renderizem, pois as referências de options
e handleSave
são sempre novas.
Depois (A Solução)
import { memo, useMemo, useCallback } from 'react';
// 1. Memoizamos os componentes filhos com React.memo()
const Dashboard = ({ options }: { options: { color: string } }) => {
console.log('Dashboard: re-render');
return <div style={{ color: options.color }}>Dashboard</div>;
};
const DashboardMemoized = memo(Dashboard);
const Button = ({
children,
...rest
}: ButtonHTMLAttributes<HTMLButtonElement>) => {
console.log('Button: re-render');
return <button {...rest}>{children}</button>;
};
const ButtonMemoized = memo(Button);
// Componente Pai
const UserProfile = () => {
// 2. Usamos useMemo para estabilizar a referência do objeto 'options'.
const options = useMemo(() => ({ color: 'blue' }), []);
// 3. Usamos useCallback para estabilizar a referência da função 'handleSave'.
const handleSave = useCallback(() => {
console.log('salvando...');
}, []);
return (
<>
<DashboardMemoized options={options} />
<ButtonMemoized onClick={handleSave}>Salvar</ButtonMemoized>
</>
);
};
Agora, o "time" está completo. useMemo
e useCallback
garantem que as props tenham referências estáveis, e React.memo
usa essa estabilidade para inteligentemente pular re-renderizações desnecessárias.
Cuidado...
Apesar de poderosa, a memoization não é uma bala de prata. O verdadeiro "inimigo" não são as re-renderizações, mas as re-renderizações desnecessárias. Aplicar memoization em tudo pode na verdade piorar a performance, pois adiciona uma sobrecarga de memória e de comparação. A regra de ouro é: meça primeiro, otimize depois. Use o React Profiler para encontrar os gargalos.
Nota
Importante ressaltar que com o React Compiler, o processo de memoization acontecerá de forma automática.
O que, na minha opinião, torna ainda mais necessário entender esse processo que acontecerá "por de baixo dos panos".
Testando seu entendimento
Conclusão
Eu lembro daquele momento "aha!" quando este conceito finalmente clicou para mim, ao depurar um componente de dashboard particularmente lento. A sensação de alívio, e o ganho de performance, foi sensacional.
Ao entender e aplicar estrategicamente a memoization, você não está apenas escrevendo "código mais limpo"; você está assumindo o controle da performance da sua aplicação e transformando a experiência do seu usuário de frustração para fluidez.
Agora, dê uma olhada nas suas aplicações. Abra o React DevTools, encontre um ponto de renderização suspeito e veja se consegue rastreá-lo até um problema de igualdade referencial.
Deixe seus feedbacks nos comentários! Grande abraço e até a próxima.
💡 Quer aprender mais sobre Next.js?
Confira meus cursos práticos e aprenda a criar aplicações profissionais do zero.
Ver CursosCurtiu? Compartilhe esse post: