React memo, useMemo e useCallback (com refs, portals e useId)
React memo, useMemo, useCallback, forwardRef, portals e useId
React memo vs useMemo vs useCallback, forwardRef, portals, useId | We.Inc
Quando usar memo, useMemo e useCallback no React, com contagem de renders ao vivo. Mais forwardRef e ref como prop no React 19, modais com createPortal, rótulos com useId e uma lista virtual de 10,000 linhas.
Envolva um componente em memo para pular renders quando as props dele não mudaram. Use useCallback para manter uma função passada como prop igual entre renders, e useMemo para guardar em cache um objeto ou um cálculo lento. Eles só fazem diferença junto com memo ou com um custo real. Meça primeiro com o React Profiler; os contadores ao vivo abaixo mostram a diferença.
Uma linha para cada
API
Guarda em cache
memo(Component)
A saída do render de um componente
useMemo(fn, deps)
Um valor
useCallback(fn, deps)
Uma função
createPortal(node, el)
Nada; renderiza em outro lugar do DOM
useId()
Nada; gera um id único e estável
As demos contam renders reais no seu navegador. Digite em cada caixa com o botão desligado e ligado e observe os contadores. A segunda metade cobre as APIs de referência que aparecem junto com o trabalho de performance: refs, portals e ids.
Encontre a interação lenta com o Profiler do React DevTools.
Consulte o quadro de decisão para o sintoma que você vê.
Aplique uma mudança, como memo no filho que renderiza de novo.
Estabilize as props que ele recebe com useCallback ou useMemo.
Faça o profiling de novo e mantenha a mudança só se os números melhorarem.
O memo compara as props de forma rasa. Espalhar um objeto novo nas props a cada render anula o efeito dele.
Mova o state para dentro do componente que o usa; muitas vezes isso elimina o novo render sem precisar de memo nenhum.
Passe children como prop. Quando um pai renderiza de novo, ele não renderiza de novo os elementos children que recebeu de cima.
O React Compiler pode adicionar essa memoização para você no build. Se o seu projeto usa o compiler, escreva código simples e deixe que ele cuide do resto.
Virtualize listas com mais de algumas centenas de linhas complexas e mantenha fixa a altura de cada linha quando puder.
Perguntas frequentes
Qual a diferença entre useMemo e useCallback?
O useMemo chama a sua função e guarda em cache o valor que ela retorna. O useCallback guarda em cache a própria função. useCallback(fn, deps) é o mesmo que useMemo(() => fn, deps).
Devo envolver todo componente em memo?
Não. Comparar props também custa tempo, e a maioria dos renders é barata. Use memo onde o Profiler mostra um componente renderizando com frequência com as mesmas props e levando um tempo perceptível.
O forwardRef está obsoleto?
No React 19, componentes de função aceitam ref como uma prop normal, então código novo não precisa de forwardRef. Ele continua funcionando, e a equipe do React disse que ele será descontinuado em uma versão futura.
Por que usar um portal para um modal?
Um modal dentro de um card com overflow hidden ou com transform pode ser cortado ou ficar por baixo de outro conteúdo. Renderizá-lo em document.body com createPortal evita as duas coisas e ainda o mantém na sua árvore React.
Posso usar useId para as keys de uma lista?
Não. O useId serve para atributos de acessibilidade como htmlFor e aria-describedby. As keys devem vir dos seus dados, como um id do banco de dados.
Como medir
Abra o React DevTools, vá até a aba Profiler, grave enquanto faz a ação lenta e pare. O flame chart mostra quais componentes renderizaram e quanto tempo cada um levou. Ative "Highlight updates when components render" para ver os novos renders enquanto clica.
Teste com um build de produção. O modo de desenvolvimento adiciona verificações que deixam tudo mais lento, e o StrictMode renderiza os componentes duas vezes de propósito.
Erros comuns
Listar como dependência um objeto que é recriado a cada render, fazendo o useMemo recalcular toda vez de qualquer jeito.
Deixar uma dependência de fora para evitar um recálculo, o que mostra dados desatualizados. A regra de lint react-hooks/exhaustive-deps pega isso.
Colocar o nó de destino de um portal em um state que não está pronto no primeiro render, fazendo o modal piscar ou dar erro no servidor.
Quadro de decisão: memo, useMemo, useCallback ou outra coisa
Qual ferramenta para qual problema
Sintoma
Use
Por quê
Um filho renderiza de novo quando o pai renderiza, embora as props dele não tenham mudado
memo(Child)
Pula o render quando todas as props são iguais (Object.is)
Um filho com memo ainda renderiza de novo porque você passa uma função nova a cada vez
useCallback
Mantém a mesma função entre renders
Um filho com memo ainda renderiza de novo porque você passa um objeto ou array novo
useMemo
Mantém o mesmo objeto até as entradas mudarem
Um cálculo lento roda a cada tecla digitada
useMemo
Guarda o resultado em cache até as entradas mudarem
Milhares de linhas deixam a rolagem ou a digitação lentas
Uma lista virtual (react-window)
Só as linhas visíveis na tela ficam no DOM
A digitação trava porque uma lista grande atualiza junto
useDeferredValue ou startTransition
Deixa o input atualizar primeiro e a lista alcançar depois
Nada está visivelmente lento
Nada
Memoizar também tem custo; meça primeiro
Contagem de renders, antes e depois
memo: pular renders quando as props são iguais
Digite na caixa. Sem memo, a lista renderiza de novo a cada tecla, mesmo que as props dela não tenham mudado. Com memo, ela renderiza uma vez.
useCallback: manter uma função igual
Passar uma arrow function inline quebra o memo, porque uma função nova é criada a cada render. O useCallback retorna a mesma até as dependências mudarem.
useMemo: guardar um cálculo lento em cache
Filtrar 20,000 produtos a cada render é trabalho desperdiçado quando só mudou um valor sem relação. O useMemo roda o filtro de novo só quando products ou query mudam.
forwardRef, ref como prop, portals e useId
forwardRef (React 18 e anteriores)
Um pai não consegue passar ref para um componente de função a menos que o componente a repasse. O forwardRef leva a ref até um elemento real.
ref como prop (React 19)
A partir do React 19, componentes de função recebem ref como qualquer outra prop, então o forwardRef não é mais necessário em código novo.
createPortal para modais e tooltips
Um portal renderiza children em outro nó do DOM, geralmente document.body, para que um modal escape de pais com overflow hidden ou z-index baixo. Os eventos do React continuam subindo pela árvore React.
useId para pares de rótulo e input
O useId gera um id único que é igual no render do servidor e do navegador, então várias cópias do mesmo componente de campo nunca entram em conflito.
Listas longas
react-window: uma lista de 10,000 linhas
O List renderiza só as linhas visíveis mais algumas extras. Cada linha recebe index e style; o style posiciona a linha e precisa ser aplicado. A demo ao lado usa a mesma ideia, escrita à mão.
Vai criar formulários? O useId combina bem com o
React Hook Form
Lista renderizada {n} vezes ({count} itens)
Envolver a lista em memo
Digite aqui
({n} renders)
Usar useCallback
Pequeno
Médio
Grande
Escolhido: {x}
Cache com useMemo
Filtrar produtos
Clique sem relação ({n})
{matches} de 20,000 correspondem. O filtro rodou {runs} vezes; a última execução levou {ms} ms.
Buscar
Focar na busca
Esta caixa tem overflow hidden, mas o modal cobre a janela inteira.
Abrir modal
Demo de portal
Fui renderizado em document.body com createPortal.