useMemo e useCallback: quando memorizar de verdade
O que cada um memoriza, por que envolver tudo deixa o app mais lento e a medição que mostra a partir de onde a memorização começa a compensar.
Você abre o Profiler — o painel que mede o tempo de cada parte da tela — porque
a busca está travando e espalha useMemo e useCallback pelo componente. O
código cresce, mas a tela continua igual — ou fica mais lenta porque uma
dependência muda em todo render. O problema não é faltar hook: é otimizar sem
saber qual trabalho está sendo repetido.
Aqui você vai separar valor de função, medir o custo real e reconhecer quando a
identidade estável ajuda. Os exemplos mantêm o painel de atrasos da
transportadora Rota Certa. Tudo rodou num MacBook Pro M4 Pro, com Node 24.16.0,
React 19.2.8 e jsdom 30.0.1, que simula no Node a árvore de elementos de uma
página. montar() e act() controlam a raiz fora do navegador;
gerarEncomendas(n) cria a base fictícia. Imports repetidos foram omitidos, e
cada saída é a que apareceu no terminal.
Qual é a diferença entre useMemo e useCallback?
useMemo reutiliza o resultado de um cálculo; useCallback, a função.
Os dois são hooks — funções especiais do React — usados como otimização. React é
a biblioteca que executa componentes, funções que descrevem partes da tela;
cada execução é um render.
Reutilizar um resultado recebe o nome de memoização. Manter o mesmo objeto ou função entre renders é estabilidade de referência: outra parte do código consegue reconhecer que recebeu exatamente a mesma peça, não apenas conteúdo parecido.
Imagine uma cozinha. As dependências são os ingredientes, useMemo guarda o
prato pronto e useCallback guarda o mesmo cartão da receita. O cartão mantém a
identidade da instrução, mas não cozinha antes de ser chamado. O limite: esse
cache — a guarda temporária do resultado — não impede o componente de
renderizar, pode ser descartado pelo React e nunca deve ser necessário para a
lógica funcionar.
Antes da sintaxe, responda: o cálculo é caro e frequente? Alguém compara a referência dessa função? Se não, o hook só acrescenta manutenção. Aqui quem decide é a medição.
Como useMemo e useCallback comparam dependências?
O React compara cada dependência com Object.is. Se todas continuam iguais,
devolve a referência guardada; se uma mudou, recalcula o useMemo ou substitui
a função do useCallback. O painel recebe rota e tema, mas ranking e
reagendamento dependem apenas da rota.
Experimente você mesmo
Antes de olhar a saída, preveja se array e função serão os mesmos quando apenas
o tema mudar e quando a rota mudar. Depois confira os valores de true e
false e relacione cada troca à lista [rota].
const encomendas = [
{ codigo: 'RC-000181', rota: 'sp-interior', atraso: 31 },
{ codigo: 'RC-000204', rota: 'sp-interior', atraso: 12 },
{ codigo: 'RC-000377', rota: 'rj-capital', atraso: 48 },
];
let anterior = {};
function PainelAtrasos({ rota, tema }) {
const atrasadas = useMemo(
() => encomendas.filter((e) => e.rota === rota).sort((a, b) => b.atraso - a.atraso),
[rota],
);
const reagendar = useCallback((codigo) => `${codigo} reagendado na rota ${rota}`, [rota]);
console.log(`render: rota=${rota} tema=${tema}`);
console.log(' useMemo devolveu:', JSON.stringify(atrasadas.map((e) => e.codigo)));
console.log(' useCallback devolveu:', reagendar('RC-000181'));
console.log(' mesmo array do render anterior? ', anterior.atrasadas === atrasadas);
console.log(' mesma função do render anterior? ', anterior.reagendar === reagendar);
anterior = { atrasadas, reagendar };
return <p className={tema}>{atrasadas.length}</p>;
}
const { root } = montar();
await act(async () => root.render(<PainelAtrasos rota="sp-interior" tema="claro" />));
await act(async () => root.render(<PainelAtrasos rota="sp-interior" tema="escuro" />));
await act(async () => root.render(<PainelAtrasos rota="rj-capital" tema="escuro" />));Leia as três respostas em ordem. No primeiro render não havia nada guardado.
No segundo, o tema mudou e a rota não: o React devolveu o mesmo array e a
mesma função do render anterior — repare que === não compara conteúdo,
compara identidade. No terceiro, a rota mudou e os dois foram refeitos.
Quem decide isso é a lista de dependências. O React guarda o array [rota] do
render anterior e compara posição a posição com Object.is — a mesma
comparação do ===, com dois ajustes para NaN e -0. Se todas as posições
baterem, ele devolve o valor velho e nem chama a sua função.
A documentação do React diz que useCallback(fn, deps) e
useMemo(() => fn, deps) são equivalentes. Dá para conferir com os dois lado a
lado, na mesma rota, com o tema alternando:
let anterior = {};
function Painel({ rota, tema }) {
const viaCallback = useCallback((codigo) => `${codigo} na rota ${rota}`, [rota]);
const viaMemo = useMemo(() => (codigo) => `${codigo} na rota ${rota}`, [rota]);
console.log(
`render ${tema}: useCallback estável? ${anterior.viaCallback === viaCallback}`,
`| useMemo estável? ${anterior.viaMemo === viaMemo}`,
);
anterior = { viaCallback, viaMemo };
return <p className={tema}>{viaCallback('RC-000181')}</p>;
}
const { root } = montar();
for (const tema of ['claro', 'escuro', 'claro']) {
await act(async () => root.render(<Painel rota="sp-interior" tema={tema} />));
}Comportamento idêntico. Repare na diferença de sintaxe, que é onde as pessoas
tropeçam: no useMemo você passa uma função que devolve o valor; no
useCallback você passa a função que é o valor.
Equivalente por fora não quer dizer o mesmo código por dentro. No react-dom
são duas funções separadas, mountMemo e mountCallback. Em desenvolvimento,
StrictMode é o modo de diagnóstico que chama a fábrica do useMemo duas vezes
para revelar cálculo impuro. O useCallback nunca chama a função; apenas a
guarda.
const chamadas = { fabricaDoMemo: 0, funcaoDoCallback: 0 };
function Painel({ rota }) {
const valor = useMemo(() => {
chamadas.fabricaDoMemo++;
return `top de ${rota}`;
}, [rota]);
const fn = useCallback(() => {
chamadas.funcaoDoCallback++;
return rota;
}, [rota]);
return <p data-fn={typeof fn}>{valor}</p>;
}
const { root } = montar();
await act(async () =>
root.render(
<StrictMode>
<Painel rota="sp-interior" />
</StrictMode>,
),
);
console.log('em StrictMode, no primeiro render:');
console.log(JSON.stringify(chamadas, null, 2));Duas chamadas contra nenhuma, num único render. Isso só machuca quem escreve
alguma coisa que muda o mundo dentro do useMemo — e é exatamente esse o
recado do React: a fábrica do useMemo precisa ser um cálculo puro, que só
lê e devolve.
Até aqui: valor, função e dependências
useMemo chama uma fábrica para obter um valor; useCallback guarda a função
sem chamá-la. Os dois reutilizam referências enquanto Object.is considera as
dependências iguais.
O useMemo impede o componente de renderizar novamente?
Não. Ele pode evitar a fábrica e o recálculo, mas o componente ainda executa e o hook ainda compara dependências. O contador separa essas duas coisas em cinquenta renders com a mesma rota e tema alternado:
const encomendas = gerarEncomendas(5000);
let renders = 0;
let calculos = 0;
function PainelAtrasos({ rota, tema }) {
renders++;
const top = useMemo(() => {
calculos++;
return encomendas
.filter((e) => e.rota === rota && !e.entregue)
.sort((a, b) => b.atraso - a.atraso)
.slice(0, 10);
}, [rota]);
return <p className={tema}>{top.length} encomendas no topo</p>;
}
const { root } = montar();
for (let i = 0; i < 50; i++) {
const tema = i % 2 ? 'claro' : 'escuro';
await act(async () => root.render(<PainelAtrasos rota="sp-interior" tema={tema} />));
}
console.log(`o componente rodou : ${renders} vezes`);
console.log(`a função do useMemo rodou: ${calculos} vez`);O cálculo rodou uma vez; o corpo do componente, cinquenta. E é justamente nos
outros quarenta e nove que mora o custo que ninguém mede: em cada um deles o
JavaScript criou o array literal [rota] e o React comparou esse array com o
do render anterior. Dá para medir essa contabilidade sem React nenhum,
reproduzindo o que o hook faz:
const encomendas = { tamanho: 100000 }; // qualquer objeto serve: o que compara é a identidade
const rota = 'sp-interior';
function contabilidade(anteriores) {
const deps = [encomendas, rota]; // o array literal é novo em todo render
if (anteriores === null) return deps;
for (let i = 0; i < deps.length; i++) {
if (!Object.is(deps[i], anteriores[i])) return deps;
}
return anteriores;
}
const VOLTAS = 20_000_000;
let anteriores = null;
for (let i = 0; i < 2_000_000; i++) anteriores = contabilidade(anteriores); // aquecimento
const amostras = [];
for (let r = 0; r < 5; r++) {
const t0 = performance.now();
for (let i = 0; i < VOLTAS; i++) anteriores = contabilidade(anteriores);
amostras.push(((performance.now() - t0) / VOLTAS) * 1e6); // nanossegundos
}
amostras.sort((a, b) => a - b);
console.log(`mediana de 5 rodadas de ${VOLTAS.toLocaleString('pt-BR')} comparações:`);
console.log(`${amostras[2].toFixed(2)} ns por render, por useMemo`);Quatro nanossegundos e meio. Mil useMemo numa mesma tela custariam menos de
cinco microssegundos por render — nada perto dos 16,7 ms que um quadro tem a
60 fps. Então não: o hook em si não é o que deixa o app lento.
Guarde essa conclusão, porque ela muda o argumento. Memorizar demais sai caro por dois outros motivos, e os dois aparecem mais para a frente neste artigo: uma lista de dependências a mais para manter correta em cada linha, e o recálculo que volta a acontecer em todo render quando essa lista está errada. O primeiro custa tempo do time; o segundo custa milissegundos de verdade.
Quando o useMemo começa a compensar?
Quando evita um cálculo caro e frequente o bastante para aparecer numa medição. Não existe quantidade universal: neste ambiente, o ganho fica claro na base de cem mil encomendas. Dois painéis permitem comparar o ranking com e sem cache:
Experimente você mesmo
Antes de ler o benchmark, ordene mentalmente as bases de 100, 1.000, 10.000 e 100.000 itens pela chance de produzir diferença perceptível. Depois confronte a previsão com os milissegundos e com a parcela do quadro de 16,7 ms.
function rankear(encomendas, rota) {
return encomendas
.filter((e) => e.rota === rota && !e.entregue)
.sort((a, b) => b.atraso - a.atraso)
.slice(0, 10);
}
function Lista({ top }) {
return (
<ul>
{top.map((e) => (
<li key={e.id}>{`${e.codigo} — ${e.atraso}h`}</li>
))}
</ul>
);
}
function PainelSemMemo({ encomendas, rota }) {
return <Lista top={rankear(encomendas, rota)} />;
}
function PainelComMemo({ encomendas, rota }) {
const top = useMemo(() => rankear(encomendas, rota), [encomendas, rota]);
return <Lista top={top} />;
}A medição descarta dez renders de aquecimento, mede quarenta, e repete tudo sete vezes para tirar a mediana — média sozinha é refém do coletor de lixo.
async function medir(Componente, encomendas, repeticoes) {
const { root } = montar();
for (let i = 0; i < 10; i++) {
await act(async () =>
root.render(<Componente encomendas={encomendas} rota="sp-interior" tick={i} />),
);
}
const t0 = performance.now();
for (let i = 0; i < repeticoes; i++) {
await act(async () =>
root.render(<Componente encomendas={encomendas} rota="sp-interior" tick={1000 + i} />),
);
}
const total = performance.now() - t0;
await act(async () => root.unmount());
return total / repeticoes;
}
const mediana = (v) => [...v].sort((a, b) => a - b)[Math.floor(v.length / 2)];
const tamanhos = [100, 1000, 10000, 100000];
const dados = Object.fromEntries(tamanhos.map((n) => [n, { sem: [], com: [] }]));
for (let r = 0; r < 7; r++) {
for (const n of tamanhos) {
const encomendas = gerarEncomendas(n);
dados[n].sem.push(await medir(PainelSemMemo, encomendas, 40));
dados[n].com.push(await medir(PainelComMemo, encomendas, 40));
}
}
console.log('encomendas | sem useMemo | com useMemo | diferença');
for (const n of tamanhos) {
const sem = mediana(dados[n].sem);
const com = mediana(dados[n].com);
const d = sem - com;
console.log(
`${String(n).padStart(10)} | ${sem.toFixed(3).padStart(8)} ms | ${com.toFixed(3).padStart(8)} ms | ${(d >= 0 ? '+' : '') + d.toFixed(3)} ms`,
);
}Traduzindo para o orçamento de um quadro a 60 fps, que é o número que importa quando a tela precisa parecer fluida:
| encomendas | sem useMemo | com useMemo | quanto do quadro de 16,7 ms o painel consome |
|---|---|---|---|
| 100 | 0,106 ms | 0,100 ms | 0,6% → 0,6% |
| 1.000 | 0,116 ms | 0,101 ms | 0,7% → 0,6% |
| 10.000 | 0,314 ms | 0,100 ms | 1,9% → 0,6% |
| 100.000 | 2,563 ms | 0,073 ms | 15,3% → 0,4% |
Até dez mil encomendas a diferença é de centésimos de milissegundo: some no
ruído da própria medição e nenhum usuário percebe. Em cem mil, o cálculo
sozinho come um sexto do quadro, e aí o useMemo deixa de ser gosto pessoal.
Só que a tabela esconde a variável mais importante, que é a frequência. Dois milissegundos e meio num painel que renderiza uma vez por clique não significam nada. Os mesmos dois milissegundos e meio num painel que re-renderiza a cada tecla digitada num campo de busca viram travamento visível. Antes de memorizar, pergunte quantas vezes por segundo aquele componente roda. Se a resposta for “uma vez a cada ação do usuário”, não memorize nada — e vale o mesmo raciocínio para ordenar um array com sort, que é a parte cara desse cálculo.
Quando o useCallback realmente evita trabalho?
Quando alguém compara a referência da função: um filho envolvido por
React.memo ou a lista de dependências de outro Hook. React.memo compara as
props — os dados que o componente filho recebe — e pode pular o render quando
elas permanecem iguais. O exemplo mede esse primeiro caso com três botões e
conta cada render:
const conta = { semMemo: 0, memoComInline: 0, memoComCallback: 0 };
function BotaoSemMemo({ onReagendar }) {
conta.semMemo++;
return <button onClick={() => onReagendar('RC-000181')}>Reagendar</button>;
}
const BotaoMemoComInline = memo(function BotaoMemoComInline({ onReagendar }) {
conta.memoComInline++;
return <button onClick={() => onReagendar('RC-000181')}>Reagendar</button>;
});
const BotaoMemoComCallback = memo(function BotaoMemoComCallback({ onReagendar }) {
conta.memoComCallback++;
return <button onClick={() => onReagendar('RC-000181')}>Reagendar</button>;
});
function Painel({ rota, tema }) {
const reagendarInline = (codigo) => `${codigo} reagendado na rota ${rota}`;
const reagendarEstavel = useCallback(
(codigo) => `${codigo} reagendado na rota ${rota}`,
[rota],
);
return (
<div className={tema}>
<BotaoSemMemo onReagendar={reagendarInline} />
<BotaoMemoComInline onReagendar={reagendarInline} />
<BotaoMemoComCallback onReagendar={reagendarEstavel} />
</div>
);
}
const { root } = montar();
for (const tema of ['claro', 'escuro', 'claro', 'escuro']) {
await act(async () => root.render(<Painel rota="sp-interior" tema={tema} />));
}
console.log('renders de cada botão em 4 renders do pai:');
console.log(JSON.stringify(conta, null, 2));O botão do meio é o que ensina. Ele está dentro de React.memo e mesmo
assim renderizou nas quatro vezes: a cada render do pai, reagendarInline é
uma função nova, o React.memo compara a prop antiga com a nova, dá diferente,
e ele desiste de pular. A memorização do componente foi anulada por uma prop
instável.
Neste exemplo, useCallback só entrega ganho porque o filho usa React.memo.
Uma função usada apenas no onClick do próprio componente não é comparada e não
ganha nada. O outro caso central é a função entrar nas dependências de outro
Hook — por exemplo, um useEffect que deve reagir só
quando ela realmente muda. Hooks customizados também costumam estabilizar as
funções que devolvem para permitir essas otimizações ao consumidor.
Por que uma dependência instável anula a memorização?
Porque uma nova referência falha na comparação por Object.is, mesmo com
conteúdo igual. O useMemo então paga a comparação e o recálculo. O erro
clássico é criar o objeto de filtros dentro do componente:
const encomendas = gerarEncomendas(100000);
const execucoes = { instavel: 0, estavel: 0 };
// desta vez o ranking aceita mais de uma rota, então recebe um objeto de filtros
function rankearPorFiltros(lista, filtros) {
return lista
.filter((e) => filtros.rotas.includes(e.rota) && !e.entregue)
.sort((a, b) => b.atraso - a.atraso)
.slice(0, 10);
}
function PainelInstavel({ tema }) {
// objeto novo a cada render: a dependência nunca é a mesma
const filtros = { rotas: ['sp-interior', 'rj-capital'] };
const top = useMemo(() => {
execucoes.instavel++;
return rankearPorFiltros(encomendas, filtros);
}, [filtros]);
return <p className={tema}>{top.length}</p>;
}A correção é mover o objeto para fora do componente. Ele não depende de nada que muda, então não tem por que nascer de novo a cada render:
// fora do componente: o mesmo objeto do primeiro ao último render
const FILTROS = { rotas: ['sp-interior', 'rj-capital'] };
function PainelEstavel({ tema }) {
const top = useMemo(() => {
execucoes.estavel++;
return rankearPorFiltros(encomendas, FILTROS);
}, []);
return <p className={tema}>{top.length}</p>;
}Cinquenta e um renders de cada versão, sobre cem mil encomendas:
Cinquenta e uma execuções em cinquenta e um renders: o useMemo da versão
instável nunca acertou o cache uma única vez. E o preço não é só o recálculo —
são 5,7 ms contra 0,07 ms, oitenta vezes mais caro, com um array de dez itens
sendo jogado fora a cada render para o coletor de lixo recolher depois.
É este o motivo pelo qual “memorizar tudo” deixa o app mais lento: quanto mais
useMemo você escreve sem olhar, maior a chance de a lista de dependências ter
um objeto, um array ou uma função criados ali mesmo — e cada um desses é um
hook que só cobra e nunca entrega.
Quando o valor depende de algo que muda, existem três saídas, nesta ordem de
preferência: subir a constante para fora do componente, como acima; quando os
filtros vêm de props, depender dos campos primitivos em vez do objeto inteiro
([rota, incluirEntregues] no lugar de [filtros], porque string e booleano o
Object.is compara por valor); ou, se o objeto precisa mesmo ser montado dentro
do componente, memorizar o próprio objeto com um useMemo antes.
Até aqui: referência estável precisa ter consumidor
Memoização só entrega valor quando evita trabalho medido ou atende uma comparação real. Dependência instável invalida o cache; referência estável que ninguém compara também não ajuda.
Como corrigir nextCreate is not a function no useMemo?
Passe uma função que devolve o cálculo, não o resultado já calculado. Sem a
arrow, rankear executa antes e entrega um array onde o React espera uma função:
const encomendas = gerarEncomendas(200);
function PainelAtrasos({ rota }) {
// faltou a arrow: isto CHAMA rankear e entrega o ARRAY para o useMemo
const top = useMemo(rankear(encomendas, rota), [rota]);
return <p>{top.length}</p>;
}TypeError: nextCreate is not a function at mountMemo (/private/tmp/rota-certa/node_modules/react-dom/cjs/react-dom-client.development.js:8777:23) at Object.useMemo (/private/tmp/rota-certa/node_modules/react-dom/cjs/react-dom-client.development.js:26216:18) at process.env.NODE_ENV.exports.useMemo (/private/tmp/rota-certa/node_modules/react/cjs/react.development.js:1251:34) at PainelAtrasos (file:///private/tmp/rota-certa/07-erro.mjs:9:15) at Object.react_stack_bottom_frame (/private/tmp/rota-certa/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20) at renderWithHooks (/private/tmp/rota-certa/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22) at updateFunctionComponent (/private/tmp/rota-certa/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19) at beginWork (/private/tmp/rota-certa/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18) at runWithFiberInDEV (/private/tmp/rota-certa/node_modules/react-dom/cjs/react-dom-client.development.js:871:30) at performUnitOfWork (/private/tmp/rota-certa/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22)
Node.js v24.16.0
nextCreate é o nome interno do primeiro argumento do useMemo — o mesmo
nextCreate que aparece na primeira linha, no var nextValue = nextCreate();.
A mensagem está dizendo, com todas as letras, que o React tentou chamar o
que você passou e o que você passou era um array. Dos dez quadros da pilha,
nove são internos do React; o seu é o quarto, at PainelAtrasos, e é o único
que interessa. A correção é de dois caracteres:
- const top = useMemo(rankear(encomendas, rota), [rota]);
+ const top = useMemo(() => rankear(encomendas, rota), [rota]);O que o React Compiler muda no useMemo e no useCallback?
Ele reduz a necessidade de memoização manual ao inserir caches durante a compilação. Isso continua sendo otimização, não garantia lógica: o componente precisa funcionar mesmo se um valor for recalculado. Na versão 1.0, o Compiler lê este componente sem hook:
function PainelAtrasos({ encomendas, rota }) {
const top = encomendas
.filter((e) => e.rota === rota && !e.entregue)
.sort((a, b) => b.atraso - a.atraso)
.slice(0, 10);
return <Lista top={top} />;
}E reescreve o arquivo. Rodando o babel-plugin-react-compiler 1.0.0 sobre esse
componente, com @babel/core 8.0.1, sai isto:
Vale ler devagar, porque é a mesma ideia deste artigo escrita por uma máquina.
_c(7) pede sete posições de cache, guardadas junto do componente. A linha
if ($[0] !== encomendas || $[1] !== rota) é exatamente a comparação que o
useMemo faria. E o compilador memorizou três coisas onde você teria
escrito uma: o ranking, a arrow passada para o filter e o próprio elemento
<Lista>. A função de comparação do sort, que não depende de nada do render,
ele simplesmente içou para fora do componente, como _temp.
Duas ressalvas honestas. O compilador desiste de otimizar componentes em que
não consegue provar que o código segue as Regras do React — mutação de valor
durante o render é o caso mais comum — e nesses arquivos você continua no
regime manual. E ele não apaga o useMemo que você já escreveu: adotar o
compilador num projeto antigo não limpa o código sozinho.
Quando vale a pena memorizar no React?
Quando uma medição mostra cálculo relevante ou quando outra API exige referência estável. A tabela resume onde a memorização paga seu custo:
| situação | memorizar? | por quê |
|---|---|---|
valor primitivo barato: uma soma, um toFixed, um template de string |
não | o cálculo custa menos que a comparação das dependências |
| lista filtrada com poucos milhares de itens, num painel que renderiza por clique | não | pela medição acima, some no ruído |
| lista pesada recalculada a cada tecla, com dezenas de milhares de itens | sim, useMemo |
é o único caso da tabela em que o número mudou de verdade |
objeto ou array passado como prop para um filho em React.memo |
sim, useMemo |
sem identidade estável, o React.memo do filho não pula nada |
função passada para um filho em React.memo |
sim, useCallback |
mesmo motivo — e só quando o React.memo existe |
função ou objeto que entra nas dependências de um useEffect |
sim, ou suba para fora | é a diferença entre o efeito rodar uma vez e rodar sempre |
| projeto com o React Compiler ligado | não | deixe o trabalho para ele e escreva o cálculo direto |
O que estudar depois de useMemo e useCallback?
O próximo passo é extrair uma regra reutilizável sem expor essa complexidade. A decisão continua sendo numérica: meça antes, memorize depois e prefira correções estruturais, como subir constantes ou dividir o componente.
Na lição de hook customizado, você verá
como esconder o cálculo e estabilizar com useCallback as funções devolvidas ao
consumidor. O guia de React mostra o caminho inteiro, e o
índice da trilha mantém a sequência lição a lição.
Perguntas frequentes
useMemo evita que o componente renderize de novo?
O React garante que o valor memorizado nunca some?
Vale usar useCallback numa função que só é chamada no onClick do próprio componente?
Com o React Compiler ligado eu ainda escrevo useMemo?
Qual a diferença entre React.memo, useMemo e useCallback?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0 com React 19.2.8 e jsdom 30.0.1, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- React — useMemo — react.dev
- React — useCallback — react.dev
- React — React Compiler — react.dev


