JavaScript Avançado

Performance em React: memo, useMemo, useCallback e Profiler na Prática

8 min de leitura

Performance em React: memo, useMemo, useCallback e Profiler na Prática

Performance em React: memo, useMemo, useCallback e Profiler React é declarativo e eficiente por padrão, mas aplicações grandes podem sofrer com renderizações desnecessárias. Nesta aula, você aprenderá as ferramentas essenciais para identificar e otimizar gargalos de performance em seus componentes. Abordaremos desde técnicas de memorização até ferramentas de diagnóstico. React.memo: Evitando Renderizações Desnecessárias React.memo é um componente de ordem superior (HOC) que memoriza um componente funcional. Se as props não mudarem, o componente não é renderizado novamente. Essa é a primeira linha de defesa contra renderizações desperdiçadas. Cuidado com objetos e funções: memo compara props superficialmente. Se você passa um objeto ou função nova a cada render, memo não funciona. Para esses casos, use a função de comparação customizada ou combine com useCallback. useMemo: Cachear Cálculos Pesados useMemo é um hook que memoriza o resultado de uma computação. Use-o quando você tem operações custosas que não precisam rodar a cada render: filtros complexos, ordenações, ou cálculos matemáticos intensivos. O array

<h2>Performance em React: memo, useMemo, useCallback e Profiler</h2>

<p>React é declarativo e eficiente por padrão, mas aplicações grandes podem sofrer com renderizações desnecessárias. Nesta aula, você aprenderá as ferramentas essenciais para identificar e otimizar gargalos de performance em seus componentes. Abordaremos desde técnicas de memorização até ferramentas de diagnóstico.</p>

<h2>React.memo: Evitando Renderizações Desnecessárias</h2>

<p>React.memo é um componente de ordem superior (HOC) que memoriza um componente funcional. Se as props não mudarem, o componente não é renderizado novamente. Essa é a primeira linha de defesa contra renderizações desperdiçadas.</p>

<pre><code class="language-jsx">// Sem memo - renderiza sempre que o pai renderiza

function UserCard({ name, email }) {

console.log(&#039;UserCard renderizado&#039;);

return (

&lt;div&gt;

&lt;h3&gt;{name}&lt;/h3&gt;

&lt;p&gt;{email}&lt;/p&gt;

&lt;/div&gt;

);

}

// Com memo - renderiza apenas se name ou email mudarem

const UserCard = React.memo(function UserCard({ name, email }) {

console.log(&#039;UserCard renderizado&#039;);

return (

&lt;div&gt;

&lt;h3&gt;{name}&lt;/h3&gt;

&lt;p&gt;{email}&lt;/p&gt;

&lt;/div&gt;

);

});

// Componente pai

function App() {

const [count, setCount] = useState(0);

return (

&lt;&gt;

&lt;button onClick={() =&gt; setCount(count + 1)}&gt;

Cliques: {count}

&lt;/button&gt;

{/ UserCard não renderiza ao clicar o botão /}

&lt;UserCard name=&quot;João&quot; email=&quot;joao@email.com&quot; /&gt;

&lt;/&gt;

);

}</code></pre>

<p><strong>Cuidado com objetos e funções</strong>: memo compara props superficialmente. Se você passa um objeto ou função nova a cada render, memo não funciona. Para esses casos, use a função de comparação customizada ou combine com useCallback.</p>

<pre><code class="language-jsx">// Problema: objeto novo a cada render

&lt;UserCard user={{ name: &quot;João&quot;, email: &quot;j@email.com&quot; }} /&gt; // Sempre renderiza

// Solução: memoizar o objeto

const user = useMemo(() =&gt; ({ name: &quot;João&quot;, email: &quot;j@email.com&quot; }), []);

&lt;UserCard user={user} /&gt; // Renderiza apenas uma vez</code></pre>

<h2>useMemo: Cachear Cálculos Pesados</h2>

<p>useMemo é um hook que memoriza o resultado de uma computação. Use-o quando você tem operações custosas que não precisam rodar a cada render: filtros complexos, ordenações, ou cálculos matemáticos intensivos.</p>

<pre><code class="language-jsx">function ProductList({ products, filter }) {

// Sem useMemo: filtro executado a cada render

const filtered = products.filter(p =&gt; p.category === filter);

return (

&lt;ul&gt;

{filtered.map(p =&gt; &lt;li key={p.id}&gt;{p.name}&lt;/li&gt;)}

&lt;/ul&gt;

);

}

// Com useMemo: filtro executado apenas quando products ou filter mudam

function ProductList({ products, filter }) {

const filtered = useMemo(

() =&gt; products.filter(p =&gt; p.category === filter),

[products, filter] // Dependências

);

return (

&lt;ul&gt;

{filtered.map(p =&gt; &lt;li key={p.id}&gt;{p.name}&lt;/li&gt;)}

&lt;/ul&gt;

);

}</code></pre>

<p>O array de dependências é crítico. Se omitir uma dependência, você terá dados desatualizados. Se incluir tudo, useMemo não traz benefício. A regra prática: inclua tudo que o callback usa e vem do escopo externo.</p>

<pre><code class="language-jsx">// Exemplo real com múltiplas dependências

const stats = useMemo(() =&gt; {

return {

total: products.reduce((sum, p) =&gt; sum + p.price, 0),

count: products.length,

avgPrice: products.reduce((sum, p) =&gt; sum + p.price, 0) / products.length

};

}, [products]); // products é a única dependência necessária</code></pre>

<h2>useCallback: Memorizar Funções</h2>

<p>useCallback é para quando você precisa passar uma função como prop para um componente memo. Sem ele, a função é recriada a cada render, invalidando a memorização do componente filho.</p>

<pre><code class="language-jsx">// Problema: handleClick recriada a cada render

function Parent() {

const [count, setCount] = useState(0);

const handleClick = () =&gt; {

setCount(count + 1);

};

// Child sempre renderiza porque handleClick é nova

return &lt;Child onClick={handleClick} /&gt;;

}

// Solução: useCallback

function Parent() {

const [count, setCount] = useState(0);

const handleClick = useCallback(() =&gt; {

setCount(c =&gt; c + 1); // Use função anterior de estado

}, []); // Sem dependências, handleClick nunca muda

return &lt;Child onClick={handleClick} /&gt;;

}

const Child = React.memo(function Child({ onClick }) {

console.log(&#039;Child renderizado&#039;);

return &lt;button onClick={onClick}&gt;Clique&lt;/button&gt;;

});</code></pre>

<p><strong>Caso de uso real</strong>: callbacks em listas de itens. Sem useCallback, cada item renderiza quando o pai muda.</p>

<pre><code class="language-jsx">function TodoList({ todos, onRemove }) {

const handleRemove = useCallback((id) =&gt; {

onRemove(id);

}, [onRemove]); // Depende de onRemove do pai

return (

&lt;ul&gt;

{todos.map(todo =&gt; (

&lt;TodoItem

key={todo.id}

todo={todo}

onRemove={handleRemove}

/&gt;

))}

&lt;/ul&gt;

);

}

const TodoItem = React.memo(function TodoItem({ todo, onRemove }) {

return (

&lt;li&gt;

{todo.text}

&lt;button onClick={() =&gt; onRemove(todo.id)}&gt;X&lt;/button&gt;

&lt;/li&gt;

);

});</code></pre>

<h2>React DevTools Profiler: Diagnóstico Profissional</h2>

<p>Não otimize sem dados. O Profiler do React DevTools mostra exatamente quais componentes renderizam, por quanto tempo e por quê. Isso diferencia um otimizador amador de um profissional.</p>

<p>Acesse o Profiler na aba &quot;Profiler&quot; do React DevTools. Clique em &quot;Record&quot;, interaja com sua aplicação, depois pare a gravação. Você verá cada render: tempo de render, causas (prop ou state mudou), e comparação antes/depois.</p>

<pre><code class="language-jsx">// Componente para análise

function Dashboard() {

const [selected, setSelected] = useState(null);

const [data, setData] = useState([]);

useEffect(() =&gt; {

// Simula fetch

setTimeout(() =&gt; setData([...Array(100)].map((_, i) =&gt; ({ id: i }))), 1000);

}, []);

return (

&lt;&gt;

&lt;button onClick={() =&gt; setSelected(selected ? null : 1)}&gt;

Toggle (causa re-render de toda árvore)

&lt;/button&gt;

&lt;MemoizedChart data={data} /&gt;

&lt;ItemList items={data} /&gt;

&lt;/&gt;

);

}

const MemoizedChart = React.memo(function Chart({ data }) {

// Se não tiver memo, renderiza desnecessariamente

return &lt;div&gt;Gráfico com {data.length} itens&lt;/div&gt;;

});</code></pre>

<p><strong>Dica profissional</strong>: Procure por &quot;Render for no reason&quot; no Profiler. Se um componente memo renderiza quando sua prop não mudou, está havendo renderização de pai desnecessária. Suba em pais e aplique técnicas de otimização em cascata.</p>

<blockquote><p>A regra de ouro: otimize apenas o que o Profiler evidenciar como problema. Prematura optimization é raiz de todo mal.</p></blockquote>

<h2>Conclusão</h2>

<p>As ferramentas apresentadas formam o arsenal do desenvolvedor React profissional. React.memo previne renderizações quando props não mudam; useMemo cacheia computações; useCallback estabiliza funções para filhos memoizados. Mas a verdadeira maestria vem do Profiler: meça sempre antes de otimizar. Componentes bem estruturados naturalmente têm boa performance. Otimizações pontuais, guiadas por dados, transformam aplicações lentas em rápidas.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://react.dev/reference/react/memo" target="_blank" rel="noopener noreferrer">React Documentation - memo</a></li>

<li><a href="https://react.dev/reference/react/useMemo" target="_blank" rel="noopener noreferrer">React Documentation - useMemo</a></li>

<li><a href="https://react.dev/reference/react/useCallback" target="_blank" rel="noopener noreferrer">React Documentation - useCallback</a></li>

<li><a href="https://react.dev/learn/react-developer-tools#profiler" target="_blank" rel="noopener noreferrer">React DevTools Profiler Guide</a></li>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance" target="_blank" rel="noopener noreferrer">Web Performance APIs - MDN</a></li>

</ul>

Comentários

Mais em JavaScript Avançado

Streams Avançados em Node.js: Transform, Duplex e Backpressure na Prática
Streams Avançados em Node.js: Transform, Duplex e Backpressure na Prática

Streams Avançados em Node.js: Transform, Duplex e Backpressure Streams são um...

O que Todo Dev Deve Saber sobre Memory Management em JavaScript: Garbage Collector e Vazamentos de Memória
O que Todo Dev Deve Saber sobre Memory Management em JavaScript: Garbage Collector e Vazamentos de Memória

Memory Management em JavaScript: Garbage Collector e Vazamentos de Memória Co...

Micro-frontends com React: Module Federation e Arquitetura Distribuída na Prática
Micro-frontends com React: Module Federation e Arquitetura Distribuída na Prática

O que são Micro-frontends e Module Federation Micro-frontends é uma arquitetu...