React & Frontend

useMemo e useCallback: Memoização Real com Análise de Custo na Prática

10 min de leitura

useMemo e useCallback: Memoização Real com Análise de Custo na Prática

Entendendo Memoização em React Memoização é uma técnica de otimização que consiste em armazenar o resultado de uma operação custosa e reutilizá-lo quando os mesmos parâmetros são fornecidos novamente. Em React, essa prática é fundamental quando você trabalha com componentes complexos ou aplicações que sofrem com renderizações desnecessárias. Antes de mergulhar em e , é crucial compreender que React renderiza componentes sempre que seu estado ou props mudam. Em muitos casos, isso é eficiente e desejável. Porém, quando você tem cálculos pesados, grandes listas de dados ou funções que são dependências críticas de outros efeitos, memoização torna-se uma ferramenta poderosa. O ponto central é este: memoização não resolve todos os problemas de performance e, quando usada incorretamente, pode até piorá-los. useMemo: Guardando Resultados de Cálculos Conceito e Sintaxe é um Hook do React que memoriza um valor calculado e só o recalcula quando suas dependências mudam. A sintaxe é simples: você passa uma função que retorna um valor e um

<h2>Entendendo Memoização em React</h2>

<p>Memoização é uma técnica de otimização que consiste em armazenar o resultado de uma operação custosa e reutilizá-lo quando os mesmos parâmetros são fornecidos novamente. Em React, essa prática é fundamental quando você trabalha com componentes complexos ou aplicações que sofrem com renderizações desnecessárias.</p>

<p>Antes de mergulhar em <code>useMemo</code> e <code>useCallback</code>, é crucial compreender que React renderiza componentes sempre que seu estado ou props mudam. Em muitos casos, isso é eficiente e desejável. Porém, quando você tem cálculos pesados, grandes listas de dados ou funções que são dependências críticas de outros efeitos, memoização torna-se uma ferramenta poderosa. O ponto central é este: memoização não resolve todos os problemas de performance e, quando usada incorretamente, pode até piorá-los.</p>

<h2>useMemo: Guardando Resultados de Cálculos</h2>

<h3>Conceito e Sintaxe</h3>

<p><code>useMemo</code> é um Hook do React que memoriza um valor calculado e só o recalcula quando suas dependências mudam. A sintaxe é simples: você passa uma função que retorna um valor e um array de dependências. Se as dependências não mudarem, React retorna o valor anterior armazenado em memória, economizando processamento.</p>

<pre><code class="language-javascript">const memoizedValue = useMemo(() =&gt; {

return expensiveCalculation(a, b);

}, [a, b]);</code></pre>

<h3>Um Exemplo Prático Real</h3>

<p>Imagine um aplicativo que filtra uma lista de usuários e calcula estatísticas. Sem memoização, a filtragem aconteceria em cada renderização, mesmo que os dados não tivessem mudado:</p>

<pre><code class="language-javascript"></code></pre>

<p>Neste exemplo, sem <code>useMemo</code>, se o componente pai re-renderizar por qualquer motivo (como uma mudança de tema ou outro estado), os filtros e cálculos aconteceriam novamente desnecessariamente. Com <code>useMemo</code>, apenas quando <code>users</code> ou <code>searchTerm</code> realmente mudarem é que o cálculo é executado.</p>

<h3>O Custo Real da Memoização</h3>

<p>Aqui está o ponto crítico que muitos desenvolvedores ignoram: <strong>memoização tem um custo</strong>. React precisa comparar as dependências a cada renderização, e se o valor memorizado for primitivo ou muito simples, o overhead dessa comparação pode ser maior que o benefício. Além disso, armazenar em memória consome recursos.</p>

<p>Use <code>useMemo</code> quando você tiver certeza de que:</p>

<ul>

<li>O cálculo é realmente custoso (operações com arrays grandes, cálculos matemáticos complexos)</li>

<li>As dependências mudam com frequência menor que a renderização do componente</li>

<li>O valor é passado como props a componentes que usam <code>React.memo</code></li>

</ul>

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

<h3>Por Que Memoizar Funções?</h3>

<p>Funções em JavaScript são objetos. Cada vez que seu componente renderiza, uma nova função é criada, mesmo que o corpo da função seja idêntico. Isso parece inofensivo, mas há cenários onde causa problemas reais: quando você passa uma função como prop para um componente <code>React.memo</code>, a nova função quebra a memoização daquele componente, causando renderizações desnecessárias.</p>

<p><code>useCallback</code> permite que você retorne a mesma instância de função entre renderizações, desde que as dependências não mudem:</p>

<pre><code class="language-javascript">const memoizedCallback = useCallback(() =&gt; {

doSomething(a, b);

}, [a, b]);</code></pre>

<h3>Exemplo Prático com Componentes Memoizados</h3>

<pre><code class="language-javascript"></code></pre>

<p>Abra o console e clique no botão &quot;Incrementar&quot;. Sem <code>useCallback</code>, você veria &quot;ButtonList renderizado&quot; a cada clique, porque uma nova função é passada como prop. Com <code>useCallback</code>, ButtonList só renderiza se a função realmente mudar (ou se <code>items</code> mudar, neste caso não muda).</p>

<h3>Dependências e Armadilhas Comuns</h3>

<p>O maior erro ao usar <code>useCallback</code> é esquecer de incluir dependências que a função realmente usa. Se sua callback precisa acessar uma variável externa, essa variável deve estar no array de dependências:</p>

<pre><code class="language-javascript"></code></pre>

<p>Note aqui que usamos <code>setCount(prevCount =&gt; ...)</code> ao invés de acessar diretamente <code>count</code>. Esta é uma prática importante: sempre que possível, use funções de atualização de estado para evitar dependências desnecessárias.</p>

<h2>Análise de Custo: Quando Usar e Quando Evitar</h2>

<h3>Medir Antes de Otimizar</h3>

<p>A regra de ouro da otimização é: <strong>meça primeiro</strong>. Use as ferramentas de profiling do React (React DevTools Profiler) para identificar onde o tempo está sendo gasto. Muitas vezes, o gargalo não está onde você acha que está.</p>

<pre><code class="language-javascript">// Exemplo de como usar React DevTools Profiler

// 1. Abra React DevTools &gt; Profiler

// 2. Grave uma interação

// 3. Procure por componentes que levam mais tempo

// 4. Identifique se é renderização ou execução do componente

function ExpensiveComponent({ data }) {

// Um cálculo que realmente é custoso

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

let sum = 0;

for (let i = 0; i &lt; data.length; i++) {

for (let j = 0; j &lt; 1000000; j++) {

sum += data[i] * j;

}

}

return sum;

}, [data]);

return &lt;div&gt;{result}&lt;/div&gt;;

}</code></pre>

<h3>Tabela de Custos</h3>

<div class="table-wrap"><table><thead><tr><th>Situação</th><th>useMemo</th><th>useCallback</th><th>Recomendação</th></tr></thead><tbody><tr><td>Cálculo simples (strings, números pequenos)</td><td>❌</td><td>❌</td><td>Não use memoização</td></tr><tr><td>Filtro/mapa de grande array</td><td></td></tr><tr><td>-</td><td>Use useMemo</td></tr><tr><td>Função passada a React.memo</td><td>-</td><td></td></tr><tr><td>Use useCallback</td></tr><tr><td>Dependência de useEffect custoso</td><td></td></tr><tr><td></td></tr><tr><td>Use conforme necessário</td></tr><tr><td>Objeto/array como prop</td><td></td></tr><tr><td>-</td><td>Use useMemo para o objeto/array</td></tr><tr><td>Estado derivado simples</td><td>❌</td><td>❌</td><td>Apenas calcule inline</td></tr></tbody></table></div>

<h3>Exemplo: Decisão de Memoização</h3>

<pre><code class="language-javascript"></code></pre>

<h2>Conclusão</h2>

<p>Três aprendizados principais levam você a dominar memoização em React:</p>

<ol>

<li><strong>Memoização é uma ferramenta, não uma solução universal.</strong> Use-a estrategicamente apenas quando mensurar e confirmar que há ganho real. Adicionar <code>useMemo</code> e <code>useCallback</code> em tudo é anti-pattern e prejudica performance.</li>

</ol>

<ol>

<li><strong>As dependências são críticas e exigem atenção.</strong> Esquecer ou incluir dependências erradas quebra a lógica do componente e causa bugs sutis. Use ferramentas como ESLint plugin para React Hooks para evitar erros.</li>

</ol>

<ol>

<li><strong>Entenda o custo: comparação de dependências, armazenamento em memória e complexidade do código.</strong> Às vezes, recalcular é mais barato que memoizar. Perfil suas aplicações com React DevTools antes de aplicar otimizações.</li>

</ol>

<h2>Referências</h2>

<ul>

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

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

<li><a href="https://web.dev/rendering-on-the-web/" target="_blank" rel="noopener noreferrer">Web.dev - React Rendering Behavior</a></li>

<li><a href="https://github.com/getify/You-Dont-Know-JS" target="_blank" rel="noopener noreferrer">Kyle Simpson - You Don&#039;t Know JS Yet: Scope &amp; Closures</a></li>

<li><a href="https://www.youtube.com/watch?v=RxjQXaDAkqg" target="_blank" rel="noopener noreferrer">Jack Herrington - React Performance Patterns</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em React & Frontend

Como Usar Micro-frontends com React: Module Federation e Arquitetura Distribuída em Produção
Como Usar Micro-frontends com React: Module Federation e Arquitetura Distribuída em Produção

O que são Micro-frontends e Por Que Module Federation? Micro-frontends repres...

useReducer em Profundidade: State Machines e Fluxo Previsível: Do Básico ao Avançado
useReducer em Profundidade: State Machines e Fluxo Previsível: Do Básico ao Avançado

Entendendo useReducer: Além do useState O é um hook do React que oferece uma...

O que Todo Dev Deve Saber sobre useTransition e useOptimistic: UX de Alta Performance em React 18
O que Todo Dev Deve Saber sobre useTransition e useOptimistic: UX de Alta Performance em React 18

Introdução ao Problema de Performance em React Quando construímos aplicações...