Memoização
O capítulo rendering é o modelo mental no qual este se baseia: quando o estado muda, React re-executa o componente e tudo abaixo dele, e normalmente isso é ótimo porque a maioria das renderizações é rápida. Às vezes não é. Um cálculo no topo de um componente pode levar tempo suficiente para você notar, ou uma subárvore de componentes pode ser cara o bastante para que re-renderizá-la a cada digitação deixe o app lento.
Para esses casos medidos, React oferece três ferramentas: useMemo, memo e useCallback, e todas as três fazem variações da mesma coisa: deixam React se lembrar de um resultado da renderização anterior e pular o retrabalho. Para usar bem qualquer uma delas, você primeiro precisa saber como JavaScript decide se duas coisas são "as mesmas".
Tipos de valor, tipos de referência e igualdade referencial
JavaScript divide valores em duas famílias. Tipos de valor (também chamados primitivos) são booleanos, números, strings e os que você encontra menos frequentemente: undefined, null, symbol e bigint. Dois deles são iguais quando têm o mesmo valor do mesmo tipo: 1 === 1 é verdadeiro. Tipos de referência (também chamados tipos complexos) são objetos, arrays e funções. Dois deles são iguais apenas quando são literalmente a mesma coisa na memória. Parecer idêntico não conta para nada.
const a = { name: 'João' }
const b = { name: 'João' }
console.log(Object.is(a, b)) // false: dois objetos separados na memória
const c = a
console.log(Object.is(a, c)) // true: ambos apontam para o mesmo objetoObject.is é o que React usa por baixo dos panos para essas comparações, e se comporta quase identicamente a === aqui. Até mesmo {} !== {}: dois literais de objeto vazio são duas alocações distintas, então falham na verificação. Isso é chamado igualdade referencial, e é o conceito raiz de todo este capítulo.
Agora conecte isso a renderização. Um componente é uma função, e cada renderização re-executa seu corpo do topo. Qualquer objeto, array ou função criada naquele corpo é totalmente novo em cada renderização:
function App() {
const styles = { backgroundColor: 'black' } // novo objeto a cada renderização
function increment() { /* ... */ } // nova função a cada renderização
// ...
}Os conteúdos podem ser idênticos de uma renderização para a próxima, mas as referências nunca são. Qualquer comparação que pergunta "essa prop mudou?" verificando Object.is responderá sim toda vez. Tudo o que se segue é sobre controlar quando essas referências mudam.
useMemo: pule um cálculo custoso
O primeiro uso de memoização não tem nada a ver com referências ainda. Um cálculo no nível superior de um componente executa em cada renderização, até mesmo renderizações acionadas por estado que o cálculo nunca toca. Se ordenar uma lista grande de produtos leva uma fatia notável de tempo, um botão contador em outro lugar do mesmo componente fica travado, porque cada clique re-executa a ordenação sem motivo algum.
useMemo resolve isso com a mesma forma de um efeito: uma função mais um array de dependências. React chama a função, se lembra do que ela retorna, e em renderizações posteriores devolve o valor memorizado sem chamar a função novamente, contanto que nada no array de dependências tenha mudado.
import { useMemo, useState } from 'react'
function ProductList({ productsData }) {
const [count, setCount] = useState(0)
const sortedProducts = useMemo(() => {
return [...productsData].sort((a, b) => a.name.localeCompare(b.name))
}, [productsData])
// ...
}O array de dependências contém tudo que o cálculo lê: aqui, apenas productsData. Mudar count re-renderiza o componente, mas React vê que productsData é a mesma e pula a ordenação inteiramente. Apenas um novo array productsData dispara um recálculo. Na demo do curso, isso reduziu a ordenação de um lag visível em cada clique para zero milissegundos em renderizações que não tocaram os dados.
memo: pule a re-renderização de um filho
useMemo coloca em cache um valor dentro de um componente. memo opera um nível acima: pode pular a re-renderização de um componente inteiro. É um componente de ordem superior, uma função que pega seu componente e retorna uma versão aprimorada dele. Você costuma vê-lo escrito React.memo em bases de código mais antigas; a importação moderna é nomeada.
import { memo } from 'react'
function Product({ name, styles }) {
// um corpo de componente custoso
}
export default memo(Product)O componente aprimorado compara suas props anteriores com suas props recebidas. Se cada prop é a mesma, React pula a re-renderização para aquela renderização pai, e a subárvore que teria renderizado é pulada com ela (com exceções, cobertas abaixo). Na app demo do curso, envolver um componente em memo reduziu uma mudança de estado de trinta e uma renderizações para uma, porque a subárvore inteira não tocada foi pulada em uma única decisão.
"A mesma" significa igualdade rasa: cada prop é comparada com Object.is. Uma prop string ou number compara por valor e se comporta como você esperaria. E é exatamente aí que o problema começa.
Por que memo silenciosamente para de funcionar
Passe a um componente envolvido com memo uma prop de objeto, e a otimização morre silenciosamente:
function App() {
const [darkMode, setDarkMode] = useState(false)
const [count, setCount] = useState(0)
const styles = {
backgroundColor: darkMode ? '#222' : '#fff',
color: darkMode ? '#fff' : '#000',
}
return <Product styles={styles} /* ... */ />
}Toda vez que count muda, App re-renderiza e constrói um objeto styles novo. Os valores dentro são idênticos, mas a igualdade referencial não se importa: Object.is(oldStyles, newStyles) é falso, memo conclui que as props mudaram, e Product re-renderiza mesmo assim. Nada causa erro, nada avisa. A memoização não faz nada.
Este é o segundo trabalho de useMemo: manter uma referência entre renderizações para que uma comparação possa ter sucesso. Envolver a criação do objeto e dar a ela a única dependência com a qual ela de fato varia:
const styles = useMemo(() => ({
backgroundColor: darkMode ? '#222' : '#fff',
color: darkMode ? '#fff' : '#000',
}), [darkMode])Agora styles é o mesmo objeto, por referência, em cada renderização até que darkMode mude. A verificação memo passa, e Product fica pulado quando apenas o contador se move. As duas ferramentas se combinam: memo no filho pergunta "as props mudaram?", e useMemo no pai garante que a resposta possa ser não.
children é uma prop como qualquer outra, e isso pega as pessoas. Filhos JSX escritos no pai são um novo objeto de elemento em cada renderização do pai, então <Card><Chart /></Card> passa a Card uma nova referência children toda vez e memo em Card nunca tem sucesso, por mais estáveis que as outras props sejam. O padrão estrutural que rendering descreve funciona do outro lado: quando o elemento é criado acima do componente que possui o estado, a re-renderização daquele componente vê o mesmo objeto de elemento e pula a ramificação sem qualquer memoização.
O que memo não pula
memo governa apenas as props que chegam do pai. Um componente memoizado ainda re-renderiza quando seu próprio estado muda e quando um contexto que lê atualiza. O contexto também alcança uma subárvore pulada: um descendente consumindo um contexto mudado re-renderiza mesmo que o invólucro memoizado acima dele tenha sido pulado.
useCallback: useMemo para funções
Funções também são tipos de referência, então uma prop de callback quebra memo exatamente da mesma forma. Defina function increment() {...} em um corpo de componente, passe-a para baixo, e o filho recebe uma referência de função totalmente nova em cada renderização do pai. useCallback existe precisamente para isso: memoiza a função em si.
const [selectedProduct, setSelectedProduct] = useState(null)
const chooseProduct = useCallback((id) => {
console.log('Novo produto selecionado')
setSelectedProduct(id)
}, [])Passe chooseProduct para baixo a um filho envolvido com memo e a otimização se mantém: useCallback(fn, deps) mantém a mesma referência de função entre renderizações até que uma dependência mude. A relação com useMemo é próxima: com useMemo o valor memorizado é qualquer coisa que sua função retorna, enquanto com useCallback o valor memorizado é a função que você passa.
Um detalhe a levar consigo: funções de definidor de estado como setSelectedProduct já têm uma identidade estável, React a garante. Passar um definidor cru para baixo como uma prop é seguro sem useCallback; o hook ganha seu lugar quando você envolve o definidor em sua própria função que faz trabalho extra primeiro.
Uma referência de função estável também importa além de memo. Se uma função aparece em um array de dependência useEffect, uma referência nova a cada renderização significa que o efeito re-executa cada renderização, como effects cobre. useCallback também silencia isso.
Meça primeiro
Confirme que há um problema de performance antes de recorrer a qualquer uma dessas ferramentas. Rendering faz o caso e expõe o fluxo de trabalho: a maioria das renderizações é barata, uma renderização que não muda nada nunca alcança o DOM, e o Profiler com CPU acelerada é como você encontra as que de fato custam algo. Memoizar um cálculo trivial como data.length custa mais do que economiza, porque o caching e a comparação são eles próprios trabalho. Corrija apenas o que pode medir, e corrija renderizações lentas antes de gastar energia reduzindo o número de renderizações.
Nota de versão
O React Compiler, que chegou a 1.0 em outubro de 2025, automatiza a maioria do que este capítulo cobre: analisa componentes em tempo de compilação e insere memoização automaticamente, então useMemo, useCallback e memo feitos à mão se tornam largamente desnecessários em projetos que o adotam. Os conceitos continuam valendo a pena entender, tanto porque bases de código existentes estão cheias desses hooks quanto porque saber o que o compilador faz por você torna seu comportamento legível em vez de mágico.
useMemo deixa React se lembrar da resposta e reutilizá-la até que as entradas mudem. memo faz algo similar para um componente inteiro: se suas props são as mesmas de última vez, React pula renderizá-lo. A parte complicada é que dois objetos ou funções que parecem idênticos ainda contam como diferentes em JavaScript, que é por isso que essas ferramentas às vezes precisam uma da outra. E antes de usar qualquer uma delas, certifique-se de que algo é de fato lento.
Próximo: Code splitting, onde a outra alavanca, o tamanho do bundle, tira sua vez.

