<h2>O Que é React.memo e Por Que Existe</h2>
<p>React.memo é um HOC (Higher Order Component) que otimiza componentes funcionais ao memorizar seu resultado. Quando um componente está envolvido com React.memo, ele só será renderizado novamente se suas props forem alteradas. Sem essa otimização, componentes são re-renderizados toda vez que o componente pai renderiza, mesmo que as props permaneçam idênticas.</p>
<p>A razão pela qual React.memo existe está enraizada no ciclo de renderização do React. Diferentemente das classes com shouldComponentUpdate, componentes funcionais não têm um ciclo de vida nativo para controlar re-renderizações. React.memo preenche essa lacuna de forma elegante, mas como veremos, sua aplicação indiscriminada pode causar mais dano do que benefício.</p>
<h2>Entendendo o Mecanismo de Comparação</h2>
<h3>Como React.memo Compara Props</h3>
<p>Por padrão, React.memo realiza uma comparação superficial (shallow comparison) das props. Isso significa que ele compara primitivos por valor e objetos/arrays por referência de memória. Se um objeto tem o mesmo conteúdo mas foi criado novamente, React.memo o considerará diferente.</p>
<pre><code class="language-javascript">const Usuario = React.memo(({ id, nome, endereco }) => {
console.log('Usuario renderizando:', nome);
return (
<div>
<p>ID: {id}</p>
<p>Nome: {nome}</p>
<p>Rua: {endereco.rua}</p>
</div>
);
});
export default function App() {
const [contador, setContador] = useState(0);
// Problema: endereco é criado a cada render
const endereco = { rua: 'Rua A', numero: 123 };
return (
<div>
<p>Contador: {contador}</p>
<button onClick={() => setContador(contador + 1)}>
Incrementar
</button>
<Usuario
id={1}
nome="João"
endereco={endereco}
/>
</div>
);
}</code></pre>
<p>Neste exemplo, o componente Usuario será renderizado a cada clique no botão, apesar das props reais (id, nome) não terem mudado. Por quê? O objeto <code>endereco</code> é recriado a cada render do componente pai, causando uma falha na comparação superficial.</p>
<h3>Função de Comparação Customizada</h3>
<p>Você pode fornecer uma função de comparação personalizada como segundo argumento para React.memo. Essa função recebe as props anteriores e as novas props, e deve retornar true se forem iguais (não renderizar) ou false se forem diferentes (renderizar).</p>
<pre><code class="language-javascript">const Usuario = React.memo(
({ id, nome, endereco }) => {
console.log('Usuario renderizando:', nome);
return (
<div>
<p>ID: {id}</p>
<p>Nome: {nome}</p>
<p>Rua: {endereco.rua}</p>
</div>
);
},
(prevProps, nextProps) => {
// Retorna true se props são iguais (NÃO renderiza)
// Retorna false se props são diferentes (renderiza)
return (
prevProps.id === nextProps.id &&
prevProps.nome === nextProps.nome &&
prevProps.endereco.rua === nextProps.endereco.rua
);
}
);
export default function App() {
const [contador, setContador] = useState(0);
const endereco = { rua: 'Rua A', numero: 123 };
return (
<div>
<p>Contador: {contador}</p>
<button onClick={() => setContador(contador + 1)}>
Incrementar
</button>
<Usuario
id={1}
nome="João"
endereco={endereco}
/>
</div>
);
}</code></pre>
<p>Agora o componente Usuario não será re-renderizado quando o contador mudar, pois a função de comparação verifica apenas as props que importam de verdade.</p>
<h2>Quando Usar React.memo Corretamente</h2>
<h3>Cenário 1: Listas Grandes com Itens Independentes</h3>
<p>React.memo brilha quando você tem listas grandes onde cada item é um componente independente. Sem memo, adicionar um item à lista faria todos os itens renderizarem novamente, mesmo os não-afetados.</p>
<pre><code class="language-javascript">const ListaItem = React.memo(({ id, texto, onDelete }) => {
console.log('ListaItem renderizando:', id);
return (
<li>
{id}: {texto}
<button onClick={() => onDelete(id)}>Deletar</button>
</li>
);
});
export default function TodoList() {
const [tarefas, setTarefas] = useState([
{ id: 1, texto: 'Estudar React' },
{ id: 2, texto: 'Fazer exercícios' },
{ id: 3, texto: 'Revisar conceitos' },
]);
const handleDelete = useCallback((id) => {
setTarefas(tarefas.filter(t => t.id !== id));
}, [tarefas]);
return (
<ul>
{tarefas.map(tarefa => (
<ListaItem
key={tarefa.id}
id={tarefa.id}
texto={tarefa.texto}
onDelete={handleDelete}
/>
))}
</ul>
);
}</code></pre>
<p>Note que usei useCallback para a função onDelete. Isso é crítico — sem ele, cada item seria re-renderizado porque onDelete seria uma nova função a cada render. Veremos isso em detalhes na próxima seção.</p>
<h3>Cenário 2: Componentes com Props Complexas Mas Raramente Alteradas</h3>
<p>Se um componente recebe muitas props mas elas mudam infrequentemente, React.memo reduz renders desnecessários.</p>
<pre><code class="language-javascript">const ConfiguracaoPainel = React.memo(({ usuario, tema, idioma, permissoes }) => {
console.log('ConfiguracaoPainel renderizando');
return (
<div style={{ color: tema.cor }}>
<h2>{usuario.nome}</h2>
<p>Idioma: {idioma}</p>
<p>Permissões: {permissoes.join(', ')}</p>
</div>
);
});
export default function App() {
const [contador, setContador] = useState(0);
const usuario = useMemo(() => ({ nome: 'João', id: 1 }), []);
const tema = useMemo(() => ({ cor: 'azul' }), []);
const idioma = 'português';
const permissoes = useMemo(() => ['ler', 'escrever'], []);
return (
<div>
<p>Render #{contador}</p>
<button onClick={() => setContador(contador + 1)}>
Incrementar
</button>
<ConfiguracaoPainel
usuario={usuario}
tema={tema}
idioma={idioma}
permissoes={permissoes}
/>
</div>
);
}</code></pre>
<p>Aqui, useMemo garante que os objetos não sejam recriados, permitindo que React.memo funcione como esperado.</p>
<h2>O Lado Sombrio: Quando Evitar React.memo</h2>
<h3>O Problema da Validação Prematura de Otimização</h3>
<p>A maioria dos projetos usa React.memo incorretamente. O maior erro é aplicar React.memo indiscriminadamente, antes de medir o impacto real. Cada comparação adicional de props tem um custo — às vezes maior que uma simples re-renderização.</p>
<pre><code class="language-javascript">// ❌ Exemplo ERRADO: React.memo em um componente simples
const Botao = React.memo(({ label, onClick }) => {
return <button onClick={onClick}>{label}</button>;
});
// Por quê está errado?
// 1. Comparar duas strings e uma função é tão rápido quanto renderizar o botão
// 2. Se onClick é criado inline, React.memo não ajuda mesmo assim</code></pre>
<p>Um componente tão simples não se beneficia de React.memo. O custo da comparação se iguala ao benefício da otimização.</p>
<h3>Quando Callback Props Causam Problemas</h3>
<p>React.memo só funciona se as props realmente não mudarem. Callbacks criados inline dentro do componente pai quebram completamente essa promessa.</p>
<pre><code class="language-javascript">// ❌ Problema clássico: callbacks inline
export default function ListaProdutos() {
const [filtro, setFiltro] = useState('');
const produtos = ['Notebook', 'Mouse', 'Teclado'];
return (
<div>
<input
value={filtro}
onChange={(e) => setFiltro(e.target.value)}
/>
{produtos.map(produto => (
<ProdutoCard
key={produto}
nome={produto}
// ❌ Esta função é criada novamente a cada render!
onComprar={() => alert(Comprou: ${produto})}
/>
))}
</div>
);
}
const ProdutoCard = React.memo(({ nome, onComprar }) => {
console.log('ProdutoCard renderizando:', nome);
return (
<div>
<p>{nome}</p>
<button onClick={onComprar}>Comprar</button>
</div>
);
});</code></pre>
<p>Neste código, React.memo falha completamente porque onComprar é uma nova função a cada render. A solução é useCallback:</p>
<pre><code class="language-javascript"></code></pre>
<h3>Quando Redux ou Context Dominam o Fluxo de Dados</h3>
<p>Se você usa Redux ou Context, React.memo pode ser contraproducente. Mudanças no store causam re-renderizações que React.memo não pode evitar.</p>
<pre><code class="language-javascript">// ❌ React.memo não ajuda aqui
const Contador = React.memo(({ valor }) => {
console.log('Contador renderizando');
return <p>Valor: {valor}</p>;
});
export default function App() {
const valor = useSelector(state => state.contador.valor);
return (
<div>
{/* Toda vez que o Redux store muda, Contador renderiza
React.memo não consegue evitar isso */}
<Contador valor={valor} />
</div>
);
}</code></pre>
<p>Neste caso, o re-render é legítimo — o valor realmente mudou no store. React.memo apenas adiciona overhead de comparação sem benefício.</p>
<h2>Padrões Avançados e Armadilhas Comuns</h2>
<h3>Array e Objeto Props: O Vilão Silencioso</h3>
<p>Mesmo com React.memo, arrays e objetos nas props causam problemas se não forem memoizados no pai.</p>
<pre><code class="language-javascript"></code></pre>
<p>A lição: React.memo e useMemo frequentemente andam juntos. Se uma prop é um objeto ou array, ela deve estar envolvida em useMemo.</p>
<h3>Quando React.memo Encontra Children</h3>
<p>Props children sempre causam problemas com React.memo porque são funções JSX recriadas a cada render.</p>
<pre><code class="language-javascript">// ❌ Problema com children
const Card = React.memo(({ titulo, children }) => {
console.log('Card renderizando:', titulo);
return (
<div>
<h2>{titulo}</h2>
{children}
</div>
);
});
export default function App() {
const [contador, setContador] = useState(0);
return (
<div>
<button onClick={() => setContador(contador + 1)}>
Incrementar: {contador}
</button>
{/ Children é recriado a cada render, quebrando React.memo /}
<Card titulo="Meu Card">
<p>Conteúdo: {contador}</p>
</Card>
</div>
);
}</code></pre>
<p>Neste caso, React.memo não vai ajudar porque children muda toda vez que o componente pai renderiza. Isso é aceitável — o Card realmente precisa renderizar quando seu conteúdo mudar.</p>
<h2>Conclusão</h2>
<p>O React.memo não é um ganho automático de performance. É uma ferramenta específica para casos específicos: listas grandes onde itens são independentes, componentes com muitas props que raramente mudam, ou componentes custosos computacionalmente. Use React.memo apenas após identificar gargalos reais com ferramentas como React Profiler. Lembre-se que toda otimização prematura tem um custo em legibilidade e complexidade do código. Combine React.memo com useCallback e useMemo quando necessário — raramente você precisará de apenas um deles.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://react.dev/reference/react/memo" target="_blank" rel="noopener noreferrer">React.memo - Documentação Oficial</a></li>
<li><a href="https://react.dev/reference/react/useCallback" target="_blank" rel="noopener noreferrer">useCallback - Documentação Oficial</a></li>
<li><a href="https://react.dev/reference/react/useMemo" target="_blank" rel="noopener noreferrer">useMemo - Documentação Oficial</a></li>
<li><a href="https://react.dev/learn/react-developer-tools#profiler" target="_blank" rel="noopener noreferrer">React Profiler - Ferramenta de Performance</a></li>
<li><a href="https://www.w3.org/webperf/" target="_blank" rel="noopener noreferrer">Web Performance Working Group - Best Practices</a></li>
</ul>
<p><!-- FIM --></p>