useCallback é um hook React que retorna uma versão memoizada de uma função que não muda entre renderizações até que suas dependências mudem. Ao contrário de uma declaração de função comum dentro de um componente (que cria uma nova função a cada renderização), useCallback estabiliza a referência da função, evitando re-renderizações desnecessárias de componentes filhos otimizados com React.memo. De acordo com React Documentation (2025), useCallback é útil apenas em conjunto com React.memo ou hooks que dependem de uma referência estável.
Pontos principais
useCallback é um hook adicionado no React 16.8 que memoiza uma função: retorna a mesma referência até que as dependências mudem. Sem useCallback, cada declaração de função dentro de um componente cria um novo objeto de função a cada renderização. Para primitivos isso é imperceptível, mas ao passar esses callbacks para componentes filhos otimizados com React.memo, cada nova referência causa uma re-renderização do componente filho.
Sintaticamente, useCallback é equivalente a useMemo para uma função: useCallback(fn, deps) é uma abreviação de useMemo(() => fn, deps). O React armazena a função memoizada no armazenamento interno do nó fiber e compara as dependências a cada renderização. Se as dependências não mudaram (Object.is para cada item), a função anterior é retornada.
De acordo com React Documentation — useCallback (2025), você não deve envolver cada função em useCallback. O hook tem seu custo: chamar o hook, comparar dependências e alocar memória para o array de dependências. Se um componente é simples e não tem árvores profundas com React.memo, useCallback só vai lentificar a aplicação. A otimização deve ser mensurável, não intuitiva.
import { useCallback } from 'react';
function Parent() {
const [count, setCount] = useState(0);
// Referência estável — mesma função até as deps mudarem
const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, []);
return <Child onClick={handleClick} />;
}
A memorização em useCallback é baseada no cache do resultado da chamada da função. O React preserva o closure criado no momento da primeira renderização e o retorna em renderizações subsequentes enquanto as dependências permanecerem inalteradas. Dentro do nó fiber, cada chamada useCallback cria um nó na lista ligada de hooks, onde as dependências anteriores e o valor memoizado são armazenados.
A comparação de dependências é realizada estritamente via Object.is — uma comparação superficial sem verificação profunda de objetos ou arrays. Se uma dependência é um objeto ou array, uma nova referência a cada renderização será considerada uma mudança. Portanto, o array de dependências deve conter valores primitivos ou referências estáveis (por exemplo, de useRef ou useMemo).
De acordo com React Core Team — Optimization Guide (2024), o custo da memorização inclui três componentes: alocar o array de dependências a cada renderização, percorrer e comparar elementos via Object.is, e a sobrecarga potencial de coleta de lixo ao recriar. Para um componente com centenas de wrappers useCallback, isso pode se tornar perceptível — portanto, a seletividade no uso do hook é crítica.
| Cenário | Sem useCallback | Com useCallback |
|---|---|---|
| Criação de função | Nova a cada renderização | A mesma com deps estáveis |
| Passar para React.memo | Filho re-renderiza | Filho não re-renderiza |
| No array de useEffect | Efeito reinicia | Efeito estável |
| Sobrecarga | Mínima | Comparação de dependências + memória |
Existe um mito generalizado de que useCallback melhora automaticamente o desempenho. Na realidade, de forma isolada (sem React.memo), useCallback até lentifica ligeiramente a aplicação devido ao custo da comparação de dependências. O hook só traz benefício real em três cenários: prevenir re-renderizações de componentes React.memo, estabilizar callbacks no useEffect e passar callbacks para hooks personalizados que dependem de igualdade de referência.
A regra é simples: até que você detecte um problema de desempenho através do React DevTools Profiler — não use useCallback. A equipe do React enfatizou repetidamente que a otimização prematura é a raiz de todos os males. Primeiro escreva código limpo sem memorização, meça, encontre o gargalo no profiler e só então adicione useCallback onde for realmente necessário.
// Otimização mensurável: Child está envolto em React.memo
const Child = React.memo(({ onClick }) => {
console.log('Child re-renderizado');
return <button onClick={onClick}>Click</button>;
});
function Parent() {
const handleClick = useCallback(() => {
console.log('clicado');
}, []);
return <Child onClick={handleClick} />;
}
De acordo com Dan Abramov — Before You memo() (2024), mais de 90% dos casos de uso de useCallback em projetos open-source são redundantes. Desenvolvedores envolvem cada função "por precaução" sem medir o efeito. A alternativa: se o componente filho é pesado e sua re-renderização é cara — React.memo + useCallback é justificado. Se o componente filho é leve — re-renderizar é mais barato que comparar dependências.
O primeiro cenário é React.memo. Se um componente filho está envolvido em React.memo e recebe um callback como prop, sem useCallback o componente filho re-renderizará em cada renderização do pai, mesmo que seus próprios dados não tenham mudado. useCallback estabiliza a referência, permitindo que React.memo pule corretamente a re-renderização.
O segundo cenário é useEffect com um callback nas dependências. Se uma função é passada para o array de dependências do useEffect, cada nova referência reiniciará o efeito. useCallback garante que a referência seja estável, e o efeito só é executado quando os dados reais mudam, não a cada renderização. Isso é especialmente importante para subscrições e requisições.
// useCallback para dependência estável de useEffect
const fetchData = useCallback(async (id) => {
const res = await fetch(`/api/${id}`);
setData(res.data);
}, []); // referência estável, nunca recria
useEffect(() => {
fetchData(props.id);
}, [props.id, fetchData]); // efeito executa apenas quando props.id muda
A principal diferença entre useCallback e useMemo é o que cada um memoiza. useCallback memoiza uma função: useCallback(fn, deps) retorna fn (a mesma ou a versão anterior). useMemo memoiza o resultado da chamada de uma função: useMemo(() => computeExpensive(a, b), [a, b]) retorna o valor computado, não uma função.
Tecnicamente, useCallback é açúcar sintático sobre useMemo: useCallback(fn, deps) é equivalente a useMemo(() => fn, deps). Esta sintaxe existe apenas para legibilidade — para que o desenvolvedor veja claramente que uma função está sendo memoizada, não um valor. Não há diferença de desempenho entre useCallback e useMemo com uma função — eles geram código idêntico.
| Hook | Memoiza | Sintaxe | Uso |
|---|---|---|---|
| useCallback | Função (referência) | useCallback(fn, deps) | Callbacks para componentes filhos |
| useMemo | Resultado de computação | useMemo(() => value, deps) | Computações caras, memorização de objetos |
// Estes são equivalentes:
const handleClick = useCallback(() => doSomething(a, b), [a, b]);
const handleClick = useMemo(() => () => doSomething(a, b), [a, b]);
O erro mais comum é envolver sem sentido todas as funções em useCallback sem React.memo nos componentes filhos. Se um componente filho não está envolvido em React.memo, ele ainda re-renderizará em cada renderização do pai, independentemente de a referência do callback mudar ou não. useCallback sem React.memo é custo sem benefício.
// ❌ Inútil: sem React.memo no filho
const handle = useCallback(() => doStuff(), []);
<Child onClick={handle} />; // Child ainda re-renderiza sem React.memo
// ❌ Stale closure: dependência faltando
const handle = useCallback(() => {
console.log(count); // count é sempre 0 — stale closure!
}, []);
// ✅ Correto: incluir dependências
const handle = useCallback(() => {
console.log(count);
}, [count]);
O problema de stale closure no useCallback é resolvido incluindo todas as variáveis usadas no array de dependências. eslint-plugin-react-hooks com exhaustive-deps verifica automaticamente que todas as variáveis do corpo do callback estão presentes no array. Se o callback usa setState, que não muda entre renderizações, pode ser incluído com segurança em deps — o React garante a estabilidade do setState.
Perguntas frequentes
Não. useCallback só faz sentido em três casos: o componente filho está envolvido em React.memo, a função é usada no array de dependências do useEffect, ou a função é passada para um hook personalizado que depende de igualdade de referência. Nos outros casos, useCallback adiciona sobrecarga sem benefício. A equipe React recomenda primeiro escrever sem otimizações e adicioná-las com base nos resultados de perfilamento.
Para componentes simples — uma nova função a cada renderização é ligeiramente mais rápida, pois useCallback gasta recursos comparando dependências e alocando o array. Para componentes com árvores profundas de React.memo, useCallback ganha ao prevenir a re-renderização de milhares de elementos filhos. Meça e compare em vez de adivinhar — use o React DevTools Profiler para uma avaliação objetiva.
Sim, useCallback funciona com funções async exatamente da mesma forma que com funções síncronas. O hook memoiza a própria função, e o resultado (uma Promise) é retornado cada vez que é chamada. Uma função async dentro de useCallback é um padrão comum para callbacks estáveis de carregamento de dados usados no useEffect: const fetchData = useCallback(async (id) => {...}, []).
Use React DevTools Profiler — ele mostra quais componentes re-renderizam e porquê. Para verificação programática, adicione console.log ou use useWhyDidYouUpdate — uma biblioteca que regista a causa da re-renderização. As principais causas: uma prop mudou (incluindo a referência do callback), o estado mudou ou o contexto mudou. Se useCallback não ajudar — verifique se todas as dependências estão corretamente especificadas.
Mesmo sem React.memo, useCallback pode ser útil em combinação com useMemo para valores de contexto. Se você está passando um objeto com funções para Context.Provider, envolva a criação do objeto em useMemo, e cada função em useCallback. Isso evita a re-renderização de todos os consumidores do contexto quando uma das funções muda. Mas para passar callbacks diretamente em props sem React.memo, não há benefício do useCallback.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também