O memo permite que um componente pule a nova renderização quando as props são as mesmas da última vez. Envolva um componente em memo(...) e, quando o pai renderizar, o React primeiro compara cada prop nova com a antiga; se todas forem iguais, ele reutiliza o resultado anterior em vez de chamar o componente de novo.
Digite no input: ele acompanha, e o Console mostra render SlowList só uma vez, da primeira renderização. Remova o wrapper memo( (e o parêntese de fechamento dele) e digite de novo: cada tecla agora espera 150 ms pela lista e registra uma renderização.
A sintaxe
import { memo } from 'react';
const MemoizedComponent = memo(SomeComponent, arePropsEqual?);
SomeComponenté qualquer componente de função. O estilo usual é envolver a definição diretamente:const Card = memo(function Card(props) { ... }). Dar nome à função interna mantém o nome no React DevTools e nas mensagens de erro.arePropsEqualé opcional e raramente necessário (veja abaixo).- O resultado é um componente que você renderiza como qualquer outro.
Por padrão, o React compara as props antigas e novas de forma superficial: ele confere cada prop com Object.is. Strings, números e booleanos são comparados pelo valor, então title="Hi" é igual a title="Hi". Objetos, arrays e funções são comparados pela referência: dois objetos com o mesmo conteúdo continuam sendo objetos diferentes.
No exemplo acima, items é declarado fora do componente, então é o mesmo array em toda renderização, e a comparação passa.
Objetos e funções novos anulam o memo
Qualquer coisa escrita como literal dentro do corpo do pai é criada de novo a cada renderização: { name: 'Ada' }, [1, 2, 3], () => save(). Passe um desses para um componente com memo e a prop nunca é igual à anterior.
Cada clique registra render Badge object prop, mas nunca a versão com string. Há três formas de corrigir o caso do objeto:
- Passe valores primitivos.
name="Ada"em vez deuser={{ name: 'Ada' }}quando o filho só precisa de um ou dois campos. - Tire as constantes do componente. Um objeto que nunca muda pode viver no nível do módulo, como
itemsno primeiro exemplo. - Guarde em cache. Envolva objetos e arrays montados a partir de props ou estado em useMemo, e funções em useCallback.
O children tem o mesmo problema. <Card><p>Hello</p></Card> cria um objeto de elemento <p> novo a cada renderização, então um Card com memo que recebe children renderiza de novo toda vez.
O que o memo não impede
O memo só olha para as props que vêm do pai. Um componente memoizado ainda renderiza quando:
- o próprio estado muda, pelo próprio
useStateouuseReducer; - um context que ele lê muda, pelo
useContext.
Clique em Unrelated e Panel é pulado. Troque o tema e ele renderiza, porque lê o context. Clique em Like e ele renderiza, porque o próprio estado mudou. Esse é o comportamento esperado: o memo serve para pular trabalho quando nada de que o componente depende mudou, e context e estado são coisas de que ele depende.
Uma função de comparação própria
O segundo argumento substitui a comparação superficial. Ele recebe as props anteriores e as próximas e retorna true quando elas devem ser tratadas como iguais, o que significa que o React pula a renderização.
const MemoChart = memo(Chart, (prev, next) => {
return (
prev.points.length === next.points.length &&
prev.points.every((p, i) => p === next.points[i]) &&
prev.color === next.color &&
prev.onSelect === next.onSelect
);
});
Você raramente precisa disso, e é fácil errar. Se a função ignora uma prop, o componente continua mostrando o valor antigo dessa prop. Ignorar uma prop de função é o pior caso: o componente pulado continua chamando a função antiga, que lê estado antigo. Compare todas as props e não faça comparações profundas em dados grandes, porque aí a comparação custa mais do que a renderização que ela economiza. Props estáveis com useMemo e useCallback são quase sempre a melhor correção.
Conferindo se o memo funciona
A verificação mais rápida é a usada nesta página: um console.log no topo do componente. Se ele imprimir quando você esperava que fosse pulado, registre as props também e compare-as com as anteriores para achar a que mudou. Em um projeto real, o React DevTools ajuda mais: ligue "Highlight updates when components render" nas configurações dele para ver quais componentes piscam a cada interação, e a aba Profiler grava cada renderização e, com a configuração correspondente ligada, por que cada componente renderizou.
Duas coisas parecem falha do memo, mas não são:
- A primeira renderização sempre acontece. O
memosó pula renderizações depois que o componente renderizou uma vez com aquelas props. - Builds de desenvolvimento podem renderizar mais. Com StrictMode, o React renderiza cada componente duas vezes em desenvolvimento para pegar código impuro. A prévia aqui roda como um build de produção, então cada linha de log acima significa uma renderização real.
Quando usar o memo
O memo vale a pena quando um componente é lento de renderizar e o pai dele renderiza com frequência com as mesmas props para ele: uma lista longa ou um gráfico ao lado de um input, uma barra lateral dentro de uma página que atualiza a cada segundo. Para componentes que renderizam rápido, ou cujas props mudam toda vez de qualquer jeito, ele adiciona uma comparação e não ganha nada. Antes de memoizar, veja se dá para reorganizar, movendo o estado para baixo ou passando JSX como children, como mostra a página sobre renderização.
O React Compiler 1.0, se o seu projeto o usa, memoiza componentes e as props deles automaticamente no build, o que torna desnecessária a maioria das chamadas manuais de memo. Sem ele, o memo funciona exatamente como descrito aqui e continua comum em código React.
Perguntas frequentes
O que o React.memo faz?
Ele retorna uma versão memoizada de um componente. Quando o pai renderiza, o React compara as novas props com as anteriores e pula a renderização do componente se todas as props forem iguais segundo Object.is.
Por que meu componente com memo continua renderizando?
Normalmente porque uma prop é um objeto, array ou função novo criado durante a renderização do pai, então nunca é igual ao anterior. O componente também renderiza quando o próprio estado muda ou quando um context que ele lê muda, com memo ou sem.
Qual é a diferença entre React.memo e useMemo?
O memo envolve um componente e pula a renderização dele. O useMemo é um hook dentro de um componente que guarda em cache um valor calculado. Eles costumam trabalhar juntos: o useMemo mantém estável uma prop de objeto para que o memo possa pular.
Devo envolver todo componente em memo?
Não. A maioria das renderizações é barata, e a comparação das props tem o próprio custo. Use-o em componentes lentos de renderizar que recebem com frequência as mesmas props.
O memo funciona com children?
Raramente. O JSX passado como children é um objeto novo a cada renderização, então um componente com memo que recebe children quase sempre renderiza de novo.