Introdução
Se você já desenvolveu aplicações React de médio a grande porte, provavelmente já enfrentou problemas de performance em algum momento. Talvez você tenha notado que sua aplicação ficou lenta, ou que a experiência do usuário não estava tão fluida quanto deveria ser.
No mundo da otimização de performance em React, existem inúmeras técnicas, bibliotecas e padrões: React.memo
, useMemo
, useCallback
, Signals, React Server Components... a lista é extensa e pode parecer intimidadora. Mas e se eu te dissesse que existe uma técnica simples e eficaz para diagnosticar problemas de performance antes mesmo de pensar em soluções complexas?
Neste artigo, vou compartilhar um método extremamente prático para identificar re-renderizações desnecessárias em seus componentes React - a raiz de muitos problemas de performance.
O Problema das Re-renderizações Silenciosas
No React, quando um componente re-renderiza, isso significa que o React está executando a função daquele componente novamente, comparando o resultado com a versão anterior e atualizando o DOM quando necessário.
Embora o React seja otimizado para fazer isso de forma eficiente, cada re-renderização ainda tem um custo de processamento. O problema surge quando componentes re-renderizam sem necessidade, ou seja, quando não há mudanças reais para refletir na interface.
Isso acontece por diversos motivos:
- Props sendo recriadas em cada renderização do componente pai
- Estados sendo atualizados com valores idênticos
- Falta de memoização em funções ou valores complexos
- Context Providers que disparam atualizações amplas demais
Mas como identificar esse problema de forma rápida, sem instrumentos complexos ou ferramentas de profiling?
A Técnica dos 10 Segundos
Existe um método impressionantemente simples para diagnosticar re-renderizações desnecessárias. Tudo o que você precisa é adicionar algumas linhas de código ao componente que deseja testar:
const renderCount = useRef(0); renderCount.current += 1; console.log("Esse componente re-renderizou", renderCount.current, "vezes");
Adicione esse código dentro do seu componente React, recarregue a página e observe o console. Agora espere por cerca de 10 segundos sem interagir com a aplicação.
Se o número de renderizações continuar subindo sem nenhuma interação do usuário, você tem um problema sério de re-renderizações desnecessárias.
As causas mais comuns de re-renderizações desnecessárias.
1. Props Instáveis
Quando um componente pai cria funções ou objetos diretamente no corpo do componente:
// Problema: novaFuncao é recriada a cada renderização do componente pai function Pai() { const novaFuncao = () => { console.log("faz algo"); }; return <Filho onAction={novaFuncao} />; }
2. Estados Atualizados de Forma Incorreta
// Problema: setCount sempre dispara uma re-renderização, mesmo quando o valor é o mesmo function incrementarDuasVezes() { setCount(count + 1); setCount(count + 1);// Usa o mesmo valor de 'count', então efetivamente não muda nada }
3. Context Sem Memoização
// Problema: todo componente que usa UserContext re-renderiza quando qualquer valor muda const UserContext = React.createContext(); function UserProvider({ children }) { const [user, setUser] = useState({}); const [preferences, setPreferences] = useState({}); // Valor do contexto é recriado a cada renderização const value = { user, setUser, preferences, setPreferences }; return ( <UserContext.Provider value={value}> {children} </UserContext.Provider> ); }
4. Effects mal configurados
// Problema: efeito sem array de dependências roda a cada renderização useEffect(() => { fetchData(); });// Sem array de dependências!
Como Resolver Re-renderizações Desnecessárias
Uma vez identificado o problema, existem várias técnicas para resolver re-renderizações desnecessárias:
1. Memoização de Componentes
Use React.memo
para evitar que componentes re-renderizem quando suas props não mudam:
const ComponenteOtimizado = React.memo(function Componente(props) { return <div>{props.texto}</div>; });
2. Memoização de Valores
Use useMemo
para valores calculados e objetos:
const objetoEstavel = useMemo(() => { return { complexo: "valor", calculado: expensiveCalculation() }; }, [dependencia1, dependencia2]);
3. Memoização de Funções
Use useCallback
para funções:
const handleClick = useCallback(() => { console.log("Clicado!", id); }, [id]);
4. Otimização de Context
Divida seus contextos por domínio e use memoização para valores:
function UserProvider({ children }) { const [user, setUser] = useState({}); const [preferences, setPreferences] = useState({}); const value = useMemo(() => { return { user, setUser, preferences, setPreferences }; }, [user, preferences]); return ( <UserContext.Provider value={value}> {children} </UserContext.Provider> ); }
5. Correto Uso de useEffect
Sempre forneça um array de dependências adequado:
useEffect(() => { fetchData(); }, [id, shouldRefetch]);// Dependências explícitas
Além da Técnica Rápida: Ferramentas Avançadas
Após identificar problemas com nossa técnica simples, você pode querer usar ferramentas mais avançadas:
- React DevTools Profiler: Fornece informações detalhadas sobre cada renderização
- why-did-you-render: Biblioteca que alerta sobre re-renderizações potencialmente desnecessárias
- Lighthouse: Para métricas gerais de performance
Devo Otimizar Tudo?
É importante lembrar que nem toda re-renderização é problemática. O React foi projetado para lidar com renderizações frequentes. A otimização prematura pode tornar seu código mais complexo sem benefícios reais.
Siga estas diretrizes:
- Meça antes de otimizar: Use a técnica descrita neste artigo
- Priorize componentes pesados: Componentes com cálculos complexos ou que renderizam muitos elementos
- Priorize componentes frequentemente atualizados: Componentes que estão no caminho de atualizações frequentes
- Teste em dispositivos reais: Especialmente dispositivos móveis de baixo/médio desempenho
Conclusão
Antes de mergulhar em soluções complexas como Signals, Server Components ou até mesmo migrar para frameworks mais novos, entenda e resolva as re-renderizações desnecessárias em sua aplicação React.
A técnica de monitoramento de renderizações com useRef
é simples, mas incrivelmente poderosa para diagnosticar problemas. Ela revela a verdade sobre o comportamento interno dos seus componentes e pode guiar otimizações que realmente importam.
Lembre-se do princípio fundamental:
Render é caro. Render sem necessidade é dívida técnica.
E você, já testou seus componentes hoje? Quantas re-renderizações desnecessárias você encontrou?