Capítulo 05: Memoização e Otimização de Performance (useMemo, useCallback)
Especialização Frontend com React & TypeScript • React 19, Hooks, Estado Global e Next.js
🗺️ Mapa Conceitual do Tópico
flowchart TD
RENDER["Componente Dispara Render"] --> MEMO_CHECK{"As dependências mudaram? (Object.is)"}
MEMO_CHECK -- "Sim" --> RECOMPUTE["Executa cálculo / Gera nova referência de função"]
MEMO_CHECK -- "Não" --> CACHED["Retorna valor em cache / Mesma referência de função"]
CACHED --> SKIP["React.memo no componente filho: PULA RE-RENDER (0ms)"]
🏛️ 1. Otimização de Performance no React
No React, otimização de performance não deve ser prematura. Por padrão, a renderização de componentes é extremamente rápida. No entanto, quando existem cálculos matemáticos pesados ou listas com milhares de nós, ferramentas de memoização tornam-se indispensáveis.
⚡ 2. useMemo vs useCallback vs React.memo
useMemo(fn, deps): Armazena em cache o resultado computado de uma função. Executa o cálculo novamente apenas se as dependências forem alteradas.useCallback(fn, deps): Armazena em cache a própria definição de função (estabilidade referencial). Evita que componentes filhos envelopados emReact.memosejam re-renderizados desnecessariamente.React.memo(Component): Função de ordem superior (HOC) que intercepta a renderização do componente e pula o ciclo se as props forem estritamente iguais (shallow equality).
📊 3. Matriz Decisória de Otimização
| Ferramenta | O que armazena em cache? | Quando utilizar? | Custo / Sobrecarga |
|---|---|---|---|
useMemo |
Valor retornado | Filtros, ordenações pesadas em grandes arrays, transformações complexas. | Alocação de array de dependências e memória de cache. |
useCallback |
Referência da função | Handlers passados para filhos memoizados com React.memo. |
Alocação de closure e comparação de dependências. |
React.memo |
Árvore de Virtual DOM | Componentes folha purificados que renderizam com alta frequência. | Comparação superficial de todas as props a cada render. |
⚠️ 4. Armadilhas Comuns e Anti-Padrões de Produção
- Memoização Prematura Universal: Envelopar todas as funções em
useCallbacke todos os objetos emuseMemoadiciona mais sobrecarga de CPU na comparação de dependências do que o custo de recriar uma função inline simples. - Dependências Instáveis: Passar um objeto literal
{ id: 1 }diretamente no array de dependências invalida a memoização a cada render, tornando ouseMemoinútil. - Esquecer o
React.memono Filho: UsaruseCallbackpara estabilizar uma função passada para um filho que não está envelopado comReact.memonão previne a re-renderização do filho.
💡 5. Diretrizes de Engenharia (Meta & React Docs)
- Otimize a Estrutura de Componentes Primeiro: Antes de recorrer à memoização, mova o estado para baixo (Move State Down) ou eleve o conteúdo como
childrenpara isolar re-renders naturalmente. - Prepare-se para o React 19 Compiler: O compilador oficial automatiza a memoização fina de valores e callbacks sem poluir o código com hooks manuais.
💻 6. Demonstração Arquitetural do Conceito
// OptimizedSearchList.tsx - useMemo com cálculo derivado
import React, { useState, useMemo, useCallback } from "react";
export function OptimizedSearchList({ data }: { data: readonly string[] }) {
const [filter, setFilter] = useState("");
const filteredList = useMemo(() => {
return data.filter(item => item.toLowerCase().includes(filter.toLowerCase()));
}, [data, filter]);
const handleSelect = useCallback((item: string) => {
console.log("Item selecionado com referência estável:", item);
}, []);
return (
<div>
<input value={filter} onChange={e => setFilter(e.target.value)} placeholder="Filtrar..." />
<p>Resultados: {filteredList.length}</p>
</div>
);
}
📋 7. Cheat-Sheet: Quando Aplicar Memoização
- ✅ SIM: Quando uma lista possui mais de 1.000 itens e precisa de ordenação/filtro em tempo real.
- ✅ SIM: Quando uma função de callback é dependência de um
useEffectem outro componente. - ❌ NÃO: Para handlers simples de cliques em botões padrão (
<button onClick={...}>). - ❌ NÃO: Para cálculos matemáticos triviais como
a + bou formatação simples de strings.
🔍 8. Diagnóstico de Performance & React DevTools Profiler
Para validar se sua memoização foi bem-sucedida:
- Inspeção de Re-render Evitado: No Profiler, componentes cinzas com listras diagonais representam nós onde o
React.memopulou o render com sucesso ("Did not render"). - Custo de Dependências: Se o tempo gasto calculando o array de dependências for superior ao tempo de execução da função pura, remova o
useMemo.
🧪 9. Casos de Teste e Cenários de Borda Recomendados
- 🔍 Listas Vazias: Assegure que arrays vazios passem pelo
useMemosem disparar cálculos matemáticos desnecessários. - 🔍 Igualdade de Referência de Handlers: Teste via Vitest se
useCallbackpreserva exatamente a mesma referência de função entre renders sucessivos. - 🔍 Mutações Profundas: Confirme que o
React.memore-renderiza quando uma prop primitiva relevante de fato é alterada.