useCallback vs useMemo: Quando e Como Usar
Os hooks useCallback e useMemo são ferramentas poderosas para otimização de performance em React, mas é comum haver confusão sobre quando usar cada um.
Objetivo do Artigo
Vamos entender as diferenças práticas entre useCallback e useMemo, quando usar cada um e como evitar otimizações desnecessárias.
useCallback - Memorizando Funções
O useCallback memoriza uma função, retornando a mesma referência enquanto suas dependências não mudarem.
useCallback-exemplo.tsxtypescript
import React, { useCallback, useState } from 'react';
interface ChildProps {
onClick: () => void;
name: string;
}
const Child = React.memo(({ onClick, name }: ChildProps) => {
console.log('Child renderizou:', name);
return (
<button onClick={onClick}>
Clique em {name}
</button>
);
});
function Parent() {
const [count, setCount] = useState(0);
const [name, setName] = useState('João');
// ❌ Sem useCallback - nova função a cada render
const handleClickBad = () => {
console.log('Clicou!');
};
// ✅ Com useCallback - mesma função enquanto deps não mudarem
const handleClickGood = useCallback(() => {
console.log('Clicou!');
}, []); // Sem dependências
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Incrementar
</button>
<Child onClick={handleClickBad} name="Bad" />
<Child onClick={handleClickGood} name="Good" />
</div>
);
}
Dica
No exemplo acima, o componente Child com handleClickBad será re-renderizado toda vez que Parent renderizar, mesmo sendo envolvido por React.memo.
useMemo - Memorizando Valores
O useMemo memoriza o resultado de um cálculo, evitando recalcular valores caros desnecessariamente.
useMemo-exemplo.tsxtypescript
import React, { useMemo, useState } from 'react';
function ExpensiveComponent() {
const [count, setCount] = useState(0);
const [items, setItems] = useState([1, 2, 3, 4, 5]);
// ❌ Sem useMemo - recalcula a cada render
const expensiveValueBad = items.reduce((acc, item) => {
// Simula cálculo pesado
for (let i = 0; i < 1000000; i++) {}
return acc + item;
}, 0);
// ✅ Com useMemo - só recalcula quando items mudar
const expensiveValueGood = useMemo(() => {
console.log('Calculando valor caro...');
return items.reduce((acc, item) => {
// Simula cálculo pesado
for (let i = 0; i < 1000000; i++) {}
return acc + item;
}, 0);
}, [items]);
return (
<div>
<p>Count: {count}</p>
<p>Expensive Value: {expensiveValueGood}</p>
<button onClick={() => setCount(count + 1)}>
Incrementar Count
</button>
<button onClick={() => setItems([...items, items.length + 1])}>
Adicionar Item
</button>
</div>
);
}
Casos Práticos de Uso
useCallback - Quando Usar
useCallback-casos.tsxtypescript
// 1. Passando funções para componentes memorizados
const MemoizedChild = React.memo(({ onSubmit }) => {
return <form onSubmit={onSubmit}>...</form>;
});
function Parent() {
const handleSubmit = useCallback((data) => {
// Lógica de submit
}, []);
return <MemoizedChild onSubmit={handleSubmit} />;
}
// 2. Dependência de outros hooks
function useCustomHook(callback) {
useEffect(() => {
// callback é dependência
callback();
}, [callback]);
}
function Component() {
const stableCallback = useCallback(() => {
// Lógica
}, []);
useCustomHook(stableCallback);
}
useMemo - Quando Usar
useMemo-casos.tsxtypescript
// 1. Cálculos caros
function DataProcessor({ data }) {
const processedData = useMemo(() => {
return data
.filter(item => item.active)
.map(item => ({
...item,
computed: heavyComputation(item)
}))
.sort((a, b) => a.priority - b.priority);
}, [data]);
return <DataTable data={processedData} />;
}
// 2. Objetos como dependências
function Component({ userId }) {
const userConfig = useMemo(() => ({
id: userId,
settings: getDefaultSettings(),
permissions: calculatePermissions(userId)
}), [userId]);
return <UserProfile config={userConfig} />;
}
// 3. Referências estáveis para Context
function Provider({ children }) {
const [state, setState] = useState(initialState);
const contextValue = useMemo(() => ({
state,
actions: {
update: (data) => setState(data),
reset: () => setState(initialState)
}
}), [state]);
return (
<Context.Provider value={contextValue}>
{children}
</Context.Provider>
);
}
Cuidado com Over-optimization
Nem sempre usar useCallback e useMemo é benéfico. Eles têm um custo próprio e só devem ser usados quando realmente necessário.
Quando NÃO Usar
quando-nao-usar.tsxtypescript
// ❌ Não use para valores primitivos simples
const Component = () => {
const [name, setName] = useState('');
// Desnecessário - string simples
const uppercaseName = useMemo(() => name.toUpperCase(), [name]);
// ✅ Melhor assim
const uppercaseNameSimple = name.toUpperCase();
};
// ❌ Não use useCallback sem React.memo
const Parent = () => {
const [count, setCount] = useState(0);
// Desnecessário se Child não é memorizado
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
return <Child onClick={handleClick} />; // Child não é React.memo
};
// ❌ Dependências que sempre mudam
const Component = () => {
const [items, setItems] = useState([]);
// Inútil - objeto sempre novo
const config = useMemo(() => ({
timestamp: Date.now(), // Sempre diferente!
items: items
}), [items]);
};
Dicas de Performance
- Meça antes de otimizar: Use React DevTools Profiler
- Combine com React.memo: useCallback é mais útil com componentes memorizados
- Cuidado com dependências: Arrays e objetos sempre "mudam"
- Considere o custo: Memorização tem overhead próprio
profiling-exemplo.tsxtypescript
// Use React DevTools para identificar re-renders desnecessários
function ProfiledComponent() {
// Envolva componentes suspeitos com Profiler
return (
<Profiler id="ExpensiveComponent" onRender={onRenderCallback}>
<ExpensiveComponent />
</Profiler>
);
}
function onRenderCallback(id, phase, actualDuration) {
console.log('Component:', id);
console.log('Phase:', phase);
console.log('Duration:', actualDuration);
}
Conclusão
useCallback e useMemo são ferramentas valiosas, mas devem ser usados com parcimônia:
- useCallback: Para estabilizar referências de funções
- useMemo: Para evitar cálculos caros desnecessários
- Ambos: Só quando há benefício real de performance
Regra de Ouro
Otimize baseado em medições reais, não em suposições. Use as ferramentas de profiling do React para identificar gargalos reais.