useCallback — conceitos-chave, o hook de memorização de funções no React

Autor: IT Sectr Publicado: 2026-07-04 Tempo de leitura: 9 min

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 para memoizar uma função, preservando a referência entre renderizações.
  • React.memo — useCallback é eficaz apenas com React.memo para prevenir re-renderizações.
  • Dependências — a função é recriada apenas quando os valores especificados no array mudam.
  • Referência estável — useCallback garante que a referência da função não mudará desnecessariamente.
  • Não abuse — o uso excessivo de useCallback sem React.memo prejudica o desempenho.

O que é useCallback no React

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.

jsx
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} />;
}

Como funciona a memorização de funções

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árioSem useCallbackCom useCallback
Criação de funçãoNova a cada renderizaçãoA mesma com deps estáveis
Passar para React.memoFilho re-renderizaFilho não re-renderiza
No array de useEffectEfeito reiniciaEfeito estável
SobrecargaMínimaComparação de dependências + memória

useCallback e desempenho

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.

jsx
// 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.

Quando usar useCallback

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.

  • Filhos com React.memo — previne re-renderização de componentes filhos memoizados ao passar callbacks.
  • Dependências de useEffect — estabiliza a função no array de dependências do efeito, evitando reinícios desnecessários.
  • Hooks personalizados — se um hook aceita um callback e depende de sua igualdade de referência, useCallback é obrigatório.
  • Valor de contexto — se uma função é passada no valor do contexto, useCallback estabiliza a referência.
jsx
// 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

useCallback vs useMemo

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.

HookMemoizaSintaxeUso
useCallbackFunção (referência)useCallback(fn, deps)Callbacks para componentes filhos
useMemoResultado de computaçãouseMemo(() => value, deps)Computações caras, memorização de objetos
jsx
// Estes são equivalentes:
const handleClick = useCallback(() => doSomething(a, b), [a, b]);
const handleClick = useMemo(() => () => doSomething(a, b), [a, b]);

Erros comuns com useCallback

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.

  • useCallback sem React.memo — o componente filho ainda re-renderiza; a otimização é inútil.
  • Dependências esquecidas — se uma variável usada dentro do callback não está em deps, o callback contém um stale closure.
  • Memoização excessiva — envolver cada função, incluindo onClick triviais com console.log.
  • Objetos e arrays em dependências — uma nova referência a cada renderização é considerada uma mudança, a memorização é inútil.
jsx
// ❌ 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

Devo envolver cada função em useCallback?

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.

O que é mais rápido — useCallback ou uma nova função a cada renderização?

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.

Pode-se usar useCallback com funções async?

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) => {...}, []).

Como depurar problemas com useCallback e React.memo?

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.

Como passar useCallback para um componente filho sem React.memo?

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

  • useCallback — um hook para memoizar uma função, retornando uma referência estável até as dependências mudarem.
  • Sinergia com React.memo — useCallback é eficaz apenas com React.memo em componentes filhos.
  • Comparação de dependências — via Object.is; objetos/arrays em deps quebram a memorização.
  • Meça, não adivinhe — adicione useCallback apenas após detetar um gargalo no profiler.
  • Closure estável — inclua todas as variáveis usadas no array de dependências, senão — stale closure.
  • useCallback vs useMemo — useCallback memoiza uma função, useMemo memoiza o resultado de uma computação.
  • Excesso — mais de 90% dos casos de uso de useCallback em projetos reais é otimização prematura.

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.

Discutir o projeto

Leia também