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 em React.memo sejam 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

  1. Memoização Prematura Universal: Envelopar todas as funções em useCallback e todos os objetos em useMemo adiciona mais sobrecarga de CPU na comparação de dependências do que o custo de recriar uma função inline simples.
  2. Dependências Instáveis: Passar um objeto literal { id: 1 } diretamente no array de dependências invalida a memoização a cada render, tornando o useMemo inútil.
  3. Esquecer o React.memo no Filho: Usar useCallback para estabilizar uma função passada para um filho que não está envelopado com React.memo nã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 children para 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 useEffect em 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 + b ou 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.memo pulou 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 useMemo sem disparar cálculos matemáticos desnecessários.
  • 🔍 Igualdade de Referência de Handlers: Teste via Vitest se useCallback preserva exatamente a mesma referência de função entre renders sucessivos.
  • 🔍 Mutações Profundas: Confirme que o React.memo re-renderiza quando uma prop primitiva relevante de fato é alterada.