useMemo e useCallback: quando usar sem exagerar no React
← Voltar para Codeshort

useMemo e useCallback: quando usar sem exagerar no React

useMemo e useCallback viraram reflexo em todo componente. Veja quando isso ajuda de verdade e quando só deixa o código mais lento.

DC
Dev Code Software
24 de julho de 2026·5 min de leitura

Abre qualquer PR de React hoje em dia e tem grande chance de encontrar um useMemo envolvendo uma soma de dois números ou um useCallback em uma função que só é usada dentro do próprio componente. Isso não é otimização. É ruído.

Índice

O reflexo que virou hábito

Em algum momento entre o React 16 e o 18, memoização virou sinônimo de "código profissional". Todo componente ganhou seu useMemo, toda função virou useCallback. O problema é que memoização tem custo. Ela guarda um valor em memória, compara dependências a cada render e só então decide se recalcula ou não. Se o cálculo que você está evitando é mais barato que essa comparação, você piorou a performance para parecer que melhorou.

Por que isso importa? Porque em produção ninguém vai medir "quantos hooks de otimização o componente tem". Vão medir se a tela trava ao digitar num input ou não.

O que useMemo realmente faz

useMemo recebe uma função e um array de dependências. Ele executa a função uma vez, guarda o resultado e só executa de novo se alguma dependência mudar entre renders.

// ❌ Memoizando algo que já é barato
const total = useMemo(() => a + b, [a, b]);

// ✓ Isso já resolve, sem overhead nenhum
const total = a + b;

A conta a + b custa menos que o processo de comparar [a, b] com o array anterior. O React ainda precisa alocar espaço para guardar o resultado memoizado e rodar essa comparação a cada render — isso tem um custo, pequeno, mas real. Para operações triviais, você está pagando um pedágio para evitar um cálculo que já era de graça.

Quando useMemo vale a pena

Existem dois cenários onde useMemo realmente entrega valor:

  1. Cálculo pesado de verdade — filtragem, ordenação ou transformação de listas grandes, cálculos matemáticos complexos, parsing de dados.
  2. Preservar identidade de referência — quando o valor memoizado é passado como dependência de outro hook ou como prop para um componente memoizado com React.memo.
// ✓ Filtrar uma lista de milhares de itens a cada render é caro
const filteredUsers = useMemo(() => {
  return users.filter(user => user.name.includes(searchTerm));
}, [users, searchTerm]);

💡 Dica: antes de memoizar, pergunte: "esse cálculo aparece no profiler do React DevTools como algo relevante?" Se a resposta é não, não memoize. Meça primeiro, otimize depois.

useCallback e a armadilha das dependências

useCallback não evita a criação da função — ela é criada em todo render de qualquer forma, é como o JavaScript funciona. O que ele evita é que uma nova referência dessa função seja passada adiante, quebrando a memoização de um componente filho.

// ❌ Isso não protege nada se o filho não for memoizado
const handleClick = useCallback(() => {
  setCount(c => c + 1);
}, []);

return <button onClick={handleClick}>Incrementar</button>;

Aqui, <button> é um elemento nativo do DOM. Ele não tem React.memo, não se importa com identidade de referência. O useCallback nesse caso não faz absolutamente nada além de adicionar complexidade.

// ✓ Agora sim: ItemCard é memoizado e depende da referência estável
const ItemCard = React.memo(function ItemCard({ onSelect, item }) {
  return <div onClick={() => onSelect(item.id)}>{item.name}</div>;
});

function List({ items }) {
  const handleSelect = useCallback((id) => {
    console.log('selecionado', id);
  }, []);

  return items.map(item => (
    <ItemCard key={item.id} item={item} onSelect={handleSelect} />
  ));
}

Isso apareceu num PR meu em 2023 e o revisor só comentou: "por quê?". Eu tinha useCallback em uma função passada para uma <div> comum. Boa pergunta, e eu não tinha resposta.

Casos reais onde faz diferença

  • Listas longas com React.memo — uma tabela com centenas de linhas, cada linha memoizada, recebendo callbacks do componente pai.
  • Dependências de outros hooks — quando o resultado de um useMemo entra no array de dependências de um useEffect, evitando loops de re-execução.
  • Bibliotecas de gráficos e canvas — recalcular pontos de um gráfico a cada render trava a interface perceptivelmente.
  • Contextos com múltiplos consumidores — memoizar o valor passado a um Context.Provider evita re-render em cascata de toda a árvore que consome aquele contexto.
// ✓ Contexto: sem isso, todo consumidor re-renderiza a cada render do Provider
const value = useMemo(() => ({ user, theme, setTheme }), [user, theme]);

return <AppContext.Provider value={value}>{children}</AppContext.Provider>;

Erros comuns

Memoizar sem medir. O dev vê o componente re-renderizando no DevTools, entra em pânico e joga useMemo em tudo, sem confirmar se aquele re-render estava causando algum problema real.

Esquecer que objetos e arrays literais quebram a memoização.

// ❌ Isso cria um objeto novo a cada render, mesmo com useCallback no filho
<ItemCard config={{ theme: 'dark' }} onSelect={handleSelect} />

// ✓ Memoize também o objeto, ou tire ele do JSX
const config = useMemo(() => ({ theme: 'dark' }), []);

Dependências incompletas. ESLint com react-hooks/exhaustive-deps existe por um motivo. Ignorar o aviso e silenciar com um comentário costuma virar bug de estado desatualizado (stale closure) três sprints depois, quando ninguém mais lembra por que aquele array estava incompleto.

⚠️ Atenção: nunca desative exhaustive-deps só para o warning sumir. Se a dependência realmente não deveria disparar o recálculo, resolva isso movendo a lógica para dentro do efeito ou usando um ref — não escondendo o aviso.

Checklist antes de usar

PerguntaSe a resposta for sim
O cálculo aparece como lento no profiler?Considere useMemo
O valor vai para um componente memoizado ou dependência de hook?Considere useMemo/useCallback
É uma operação O(1) simples (soma, concatenação)?Não memoize
A função vai para um elemento nativo (button, div)?Não use useCallback
Você não mediu nada, só "acha" que vai ajudar?Não memoize ainda

FAQ

useMemo e useCallback evitam re-render do componente atual? Não. Eles não impedem que o próprio componente re-renderize. Eles evitam recalcular um valor ou recriar uma referência de função, o que só importa se esse valor for usado por algo que se importa com identidade de referência, como React.memo ou o array de dependências de outro hook.

Vale usar useMemo em todo componente "por garantia"? Não. Cada useMemo adiciona uma comparação de dependências e ocupa memória. Em componentes pequenos e cálculos triviais, isso é overhead puro, sem nenhum ganho perceptível.

React Compiler não resolve isso automaticamente? O React Compiler, quando habilitado no projeto, memoiza automaticamente boa parte desses casos em tempo de build. Isso reduz a necessidade de useMemo/useCallback manuais, mas entender o que ele está fazing por baixo dos panos ainda ajuda a debugar performance quando algo foge do esperado.

Como eu meço se realmente preciso memoizar? Use o profiler do React DevTools, ative "Highlight updates when components render" e observe quais componentes re-renderizam sem necessidade. Meça antes e depois de aplicar a memoização — se a diferença não aparece no gráfico, provavelmente não valia a pena.

useCallback resolve problema de closure desatualizada? Não resolve sozinho. Se as dependências estiverem corretas, o useCallback até ajuda a expor o problema mais cedo, porque o ESLint vai avisar quando uma variável usada dentro da função não está na lista de dependências.

Próximos passos

  • Abra o React DevTools Profiler no seu projeto atual e procure componentes que re-renderizam sem necessidade antes de adicionar qualquer memoização.
  • Remova useMemo/useCallback de cálculos triviais que você encontrar em código existente — teste se a performance muda (não vai mudar).
  • Se seu projeto já usa React 19 ou está migrando para lá, avalie o React Compiler antes de espalhar memoização manual por toda a base de código.