Já trabalhei em codebases React que eram rápidas desde o primeiro dia e outras que se tornaram lentas assim que atingiram algumas centenas de componentes. A diferença raramente era o framework. Era os hábitos.
Isto não é uma lista de micro-otimizações para bottlenecks teóricos. São os padrões a que recorro quando algo parece lento, na ordem aproximada em que os uso.
Começa pelo Profiler, Não por um Palpite
Antes de tocar numa única linha de código, abre o React DevTools e grava uma sessão do Profiler. Clica nas partes que parecem lentas. O flame graph mostra-te exatamente quais os componentes que estão a re-renderizar e quanto tempo cada renderização demora.
Em nove casos em dez, a parte lenta não é onde pensavas. Otimizar sem fazer profiling primeiro é como mover mobília num quarto às escuras.
Re-renderizações Desnecessárias São o Verdadeiro Problema
Um componente re-renderiza quando o seu pai re-renderiza, mesmo que as suas próprias props não tenham mudado. Em árvores pequenas isto não é problema. Numa árvore de componentes grande, o impacto acumula-se rapidamente.
React.memo envolve um componente para que só re-renderize quando as suas props mudam de facto. Usa-o em componentes que são caros de renderizar e que recebem as mesmas props com frequência.
const ArticleCard = React.memo(function ArticleCard({ title, excerpt }) {
return (
<div>
<h2>{title}</h2>
<p>{excerpt}</p>
</div>
);
});A ressalva: React.memo faz uma comparação superficial. Se passares um novo objeto ou referência de função a cada renderização, não vai ajudar. O que nos leva ao padrão seguinte.
Estabiliza Referências com useMemo e useCallback
Sempre que um componente renderiza, objetos e funções inline são recriados. Se os passares como props a um filho memoizado, a verificação do memo falha sempre.
// Isto cria um novo objeto a cada renderização
const style = { color: 'blue' };
// Isto só cria um novo objeto quando a cor muda
const style = useMemo(() => ({ color: cor }), [cor]);
// A mesma ideia para callbacks
const handleClick = useCallback(() => {
doSomething(id);
}, [id]);Não envolvas tudo em useMemo por defeito. Tem overhead. Usa-o quando tens um filho memoizado que está a re-renderizar desnecessariamente, ou quando estás a fazer cálculos genuinamente caros dentro de um componente.
Code Splitting com React.lazy
Se o teu bundle for grande, os utilizadores pagam o custo antecipadamente. O code splitting permite carregar partes da aplicação apenas quando são necessárias.
const SettingsPage = React.lazy(() => import('./SettingsPage'));
function App() {
return (
<Suspense fallback={<Spinner />}>
<SettingsPage />
</Suspense>
);
}Funciona melhor nas fronteiras de rotas. Divide por página, não por componente. Uma página de definições que a maioria dos utilizadores nunca abre não deve estar no bundle inicial.
Virtualiza Listas Longas
Renderizar uma lista de 10.000 itens significa 10.000 nós no DOM. O browser não gosta disso. A virtualização renderiza apenas o que está visível no ecrã.
Bibliotecas como react-window e react-virtual tratam disso bem. Se tens uma tabela ou lista com mais de algumas centenas de linhas, a virtualização não é opcional.
import { FixedSizeList } from 'react-window';
<FixedSizeList
height={600}
width="100%"
itemCount={items.length}
itemSize={48}
>
{({ index, style }) => (
<div style={style}>{items[index].name}</div>
)}
</FixedSizeList>Move o Estado para Baixo, Não para Cima
Estado que vive no topo da árvore faz com que toda a árvore re-renderize quando muda. Se apenas um componente precisa de um pedaço de estado, coloca o estado aí.
Parece óbvio, mas é um dos problemas de performance mais comuns que vejo. Um input de pesquisa ligado a estado global que re-renderiza todo o layout da página a cada tecla pressionada é um exemplo clássico. Move o input e o seu estado para um componente autónomo.
O Context Não É uma Bala de Prata de Performance
O React Context é excelente para coisas que mudam raramente, como tema ou locale. É uma má escolha para estado que muda frequentemente, porque todos os consumidores do context re-renderizam quando o valor muda.
Se estás a usar context para algo que se atualiza com interação do utilizador, considera um gestor de estado dedicado ou divide o context em partes mais granulares.
O Que Não Otimizar
A maioria dos componentes não precisa de nada disto. Um componente que renderiza alguns parágrafos e um botão não é o teu problema. Otimização prematura desperdiça tempo e adiciona complexidade.
O padrão que sigo: publica, faz profiling, corrige o que os dados dizem que é lento. Na maioria das aplicações, 80% da melhoria de performance percebida vem de corrigir três ou quatro pontos críticos, não de memoizar tudo em todo o lado.
O React é rápido por defeito. Os padrões acima são para quando deixa de o ser, não para blindagem preventiva.



