Truques para melhorar performance em React
A otimização de performance em React é um tópico essencial para desenvolvedores que buscam criar aplicações rápidas e responsivas. Neste artigo, exploraremos truques práticos para melhorar a performance, cobrindo desde memorização até profiling avançado.
1. Minimizando Re-renderizações com Memorização
Re-renderizações desnecessárias são uma das principais causas de lentidão em aplicações React. Felizmente, o React oferece ferramentas nativas para evitar esse problema.
React.memo
React.memo é um higher-order component que impede re-renderizações de componentes puros quando suas props não mudam:
import React from 'react';
const ExpensiveComponent = React.memo(({ data }) => {
console.log('Renderizou!');
return <div>{data}</div>;
});
function App() {
const [count, setCount] = React.useState(0);
const data = React.useMemo(() => ({ value: 'estável' }), []);
return (
<div>
<button onClick={() => setCount(count + 1)}>Clique</button>
<ExpensiveComponent data={data} />
</div>
);
}
useMemo e useCallback
useMemo memoriza valores calculados, enquanto useCallback estabiliza referências de funções:
import React, { useMemo, useCallback } from 'react';
function SearchResults({ query, items }) {
const filteredItems = useMemo(() => {
return items.filter(item => item.includes(query));
}, [query, items]);
const handleClick = useCallback((id) => {
console.log('Item clicado:', id);
}, []);
return (
<ul>
{filteredItems.map(item => (
<li key={item.id} onClick={() => handleClick(item.id)}>
{item.name}
</li>
))}
</ul>
);
}
2. Otimização de Listas e Grandes Conjuntos de Dados
Renderizar listas enormes pode degradar drasticamente a performance. A virtualização é a solução ideal.
Virtualização com react-window
import { FixedSizeList as List } from 'react-window';
const Row = ({ index, style }) => (
<div style={style}>Item {index + 1}</div>
);
function VirtualizedList() {
return (
<List
height={400}
itemCount={10000}
itemSize={35}
width={300}
>
{Row}
</List>
);
}
Key prop correta
Sempre use keys estáveis e únicas para evitar reconciliação ineficiente:
// Ruim
{items.map((item, index) => <Item key={index} />)}
// Bom
{items.map(item => <Item key={item.id} />)}
Lazy loading com Intersection Observer
import { useEffect, useRef, useState } from 'react';
function LazyImage({ src, alt }) {
const [isVisible, setIsVisible] = useState(false);
const imgRef = useRef();
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
observer.disconnect();
}
});
observer.observe(imgRef.current);
return () => observer.disconnect();
}, []);
return (
<div ref={imgRef}>
{isVisible ? <img src={src} alt={alt} /> : <div>Carregando...</div>}
</div>
);
}
3. Gerenciamento Eficiente de Estado e Contexto
Contextos mal estruturados podem causar re-renderizações em cascata.
Divisão de Contextos
Em vez de um contexto gigante, divida em contextos menores:
const UserContext = React.createContext();
const ThemeContext = React.createContext();
function App() {
return (
<UserContext.Provider value={user}>
<ThemeContext.Provider value={theme}>
<MainComponent />
</ThemeContext.Provider>
</UserContext.Provider>
);
}
Zustand para estado atômico
import { create } from 'zustand';
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}));
function Counter() {
const count = useStore((state) => state.count);
return <div>{count}</div>;
}
4. Code Splitting e Lazy Loading de Componentes
Reduza o bundle inicial carregando componentes sob demanda.
React.lazy e Suspense
import React, { Suspense } from 'react';
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Carregando...</div>}>
<HeavyComponent />
</Suspense>
);
}
Lazy loading com React Router
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import React, { Suspense } from 'react';
const Home = React.lazy(() => import('./pages/Home'));
const Dashboard = React.lazy(() => import('./pages/Dashboard'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Carregando...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
5. Otimização de Imagens e Assets
Imagens são frequentemente os maiores assets em uma página.
Lazy loading nativo
<img src="large-image.jpg" loading="lazy" alt="Descrição" />
Pré-carregamento de imagens críticas
<link rel="preload" href="hero-image.webp" as="image" />
6. Profiling e Identificação de Gargalos
Use ferramentas para identificar problemas de performance.
React DevTools Profiler
// No console do navegador com React DevTools
// Acesse a aba "Profiler" e grave interações
why-did-you-render
import React from 'react';
if (process.env.NODE_ENV === 'development') {
const whyDidYouRender = require('@welldone-software/why-did-you-render');
whyDidYouRender(React, {
trackAllPureComponents: true,
});
}
7. Boas Práticas de Renderização e Estilização
Evitar funções anônimas em props
// Ruim
<Button onClick={() => handleClick(id)} />
// Bom
<Button onClick={handleClick} />
useRef para valores mutáveis
function Timer() {
const countRef = useRef(0);
useEffect(() => {
const interval = setInterval(() => {
countRef.current += 1;
}, 1000);
return () => clearInterval(interval);
}, []);
return <div>Timer rodando...</div>;
}
Conclusão
A otimização de performance em React envolve múltiplas estratégias, desde memorização até virtualização e code splitting. Comece identificando gargalos com o Profiler, aplique memorização onde necessário, virtualize listas grandes e divida seu código em chunks menores. Lembre-se: otimize apenas quando houver problemas reais de performance, evitando complexidade desnecessária.
Referências
- React Documentation - Optimizing Performance — Documentação oficial do React sobre técnicas de otimização, incluindo memo, useMemo e useCallback
- react-window - GitHub — Biblioteca para virtualização de listas grandes em React, com exemplos práticos
- Zustand Documentation — Documentação oficial do Zustand para gerenciamento de estado atômico e seletivo
- React DevTools Profiler - Chrome Web Store — Extensão para profiling e identificação de gargalos de performance em React
- Web Vitals - web.dev — Guia completo sobre métricas de performance web (FCP, LCP, TBT) e como monitorá-las
- why-did-you-render - GitHub — Biblioteca para detectar re-renderizações desnecessárias em componentes React
- React.lazy and Suspense - React Documentation — Documentação oficial sobre code splitting com lazy loading em React