useCallback — conceptos clave, el hook de memorización de funciones en React

Autor: IT Sectr Publicado: 2026-07-04 Tiempo de lectura: 9 min

useCallback es un hook de React que devuelve una versión memoizada de una función que no cambia entre renderizados hasta que sus dependencias cambien. A diferencia de una declaración de función normal dentro de un componente (que crea una nueva función en cada renderizado), useCallback estabiliza la referencia a la función, evitando rerenderizados innecesarios de componentes hijos optimizados con React.memo. Según React Documentation (2025), useCallback solo es útil en combinación con React.memo o hooks que dependen de una referencia estable.

Puntos clave

  • useCallback — un hook para memoizar una función, conservando la referencia entre renderizados.
  • React.memo — useCallback solo es efectivo con React.memo para prevenir rerenderizados.
  • Dependencias — la función se recrea solo cuando cambian los valores especificados en el array.
  • Referencia estable — useCallback garantiza que la referencia a la función no cambiará innecesariamente.
  • No abusar — el uso excesivo de useCallback sin React.memo perjudica el rendimiento.

Qué es useCallback en React

useCallback es un hook añadido en React 16.8 que memoiza una función: devuelve la misma referencia hasta que las dependencias cambien. Sin useCallback, cada declaración de función dentro de un componente crea un nuevo objeto de función en cada renderizado. Para primitivas esto es imperceptible, pero al pasar estos callbacks a componentes hijos optimizados con React.memo, cada nueva referencia provoca un rerenderizado del componente hijo.

Sintácticamente, useCallback es equivalente a useMemo para una función: useCallback(fn, deps) es una abreviatura de useMemo(() => fn, deps). React almacena la función memoizada en el almacenamiento interno del nodo fiber y compara las dependencias en cada renderizado. Si las dependencias no han cambiado (Object.is para cada elemento), se devuelve la función anterior.

Según React Documentation — useCallback (2025), no debes envolver cada función en useCallback. El hook tiene su coste: llamar al hook, comparar dependencias y asignar memoria para el array de dependencias. Si un componente es simple y no tiene árboles profundos con React.memo, useCallback solo ralentizará la aplicación. La optimización debe ser medible, no intuitiva.

jsx
import { useCallback } from 'react';

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

    // Referencia estable — misma función hasta que cambien las deps
    const handleClick = useCallback(() => {
        setCount(prev => prev + 1);
    }, []);

    return <Child onClick={handleClick} />;
}

Cómo funciona la memorización de funciones

La memorización en useCallback se basa en el almacenamiento en caché del resultado de la llamada a la función. React conserva el closure creado en el primer renderizado y lo devuelve en renderizados posteriores mientras las dependencias permanezcan sin cambios. Dentro del nodo fiber, cada llamada a useCallback crea un nodo en la lista enlazada de hooks, donde se almacenan las dependencias anteriores y el valor memoizado.

La comparación de dependencias se realiza estrictamente mediante Object.is — una comparación superficial sin verificación profunda de objetos o arrays. Si una dependencia es un objeto o array, una nueva referencia en cada renderizado se considerará un cambio. Por lo tanto, el array de dependencias debe contener valores primitivos o referencias estables (por ejemplo, de useRef o useMemo).

Según React Core Team — Optimization Guide (2024), el coste de la memorización incluye tres componentes: asignar el array de dependencias en cada renderizado, recorrer y comparar elementos mediante Object.is, y la posible sobrecarga de recolección de basura al recrear. Para un componente con cientos de envoltorios useCallback, esto puede volverse notable — por lo que la selectividad en el uso del hook es crítica.

EscenarioSin useCallbackCon useCallback
Creación de funciónNueva en cada renderizadoLa misma con deps estables
Pasar a React.memoEl hijo se rerenderizaEl hijo no se rerenderiza
En array de useEffectEl efecto se reiniciaEl efecto es estable
SobrecargaMínimaComparación de dependencias + memoria

useCallback y el rendimiento

Existe un mito generalizado de que useCallback mejora automáticamente el rendimiento. En realidad, de forma aislada (sin React.memo), useCallback incluso ralentiza ligeramente la aplicación debido al coste de la comparación de dependencias. El hook solo aporta un beneficio real en tres escenarios: prevenir rerenderizados de componentes React.memo, estabilizar callbacks en useEffect y pasar callbacks a hooks personalizados que dependen de la igualdad de referencias.

La regla es simple: hasta que no detectes un problema de rendimiento con React DevTools Profiler, no uses useCallback. El equipo de React ha enfatizado repetidamente que la optimización prematura es la raíz de todos los males. Primero escribe código limpio sin memorización, mide, encuentra el cuello de botella en el perfilador y solo entonces añade useCallback donde sea realmente necesario.

jsx
// Optimización medible: Child está envuelto en React.memo
const Child = React.memo(({ onClick }) => {
    console.log('Child rerenderizado');
    return <button onClick={onClick}>Click</button>;
});

function Parent() {
    const handleClick = useCallback(() => {
        console.log('clicado');
    }, []);
    return <Child onClick={handleClick} />;
}

Según Dan Abramov — Before You memo() (2024), más del 90% de los casos de uso de useCallback en proyectos open-source son redundantes. Los desarrolladores envuelven cada función "por si acaso" sin medir el efecto. La alternativa: si el componente hijo es pesado y su rerenderizado es costoso — React.memo + useCallback está justificado. Si el componente hijo es ligero — rerenderizar es más barato que comparar dependencias.

Cuándo usar useCallback

El primer escenario es React.memo. Si un componente hijo está envuelto en React.memo y recibe un callback como prop, sin useCallback el componente hijo se rerenderizará en cada renderizado del padre, incluso si sus propios datos no han cambiado. useCallback estabiliza la referencia, permitiendo que React.memo omita correctamente el rerenderizado.

El segundo escenario es useEffect con un callback en dependencias. Si una función se pasa al array de dependencias de useEffect, cada nueva referencia reiniciará el efecto. useCallback asegura que la referencia sea estable, y el efecto solo se ejecuta cuando los datos reales cambian, no en cada renderizado. Esto es especialmente importante para suscripciones y peticiones.

  • Hijos con React.memo — previene el rerenderizado de componentes hijos memoizados al pasar callbacks.
  • Dependencias de useEffect — estabiliza la función en el array de dependencias del efecto, evitando reinicios innecesarios.
  • Hooks personalizados — si un hook acepta un callback y depende de su igualdad de referencia, useCallback es obligatorio.
  • Valor de contexto — si se pasa una función en el valor del contexto, useCallback estabiliza la referencia.
jsx
// useCallback para dependencia estable de useEffect
const fetchData = useCallback(async (id) => {
    const res = await fetch(`/api/${id}`);
    setData(res.data);
}, []); // referencia estable, nunca se recrea

useEffect(() => {
    fetchData(props.id);
}, [props.id, fetchData]); // el efecto se ejecuta solo cuando props.id cambia

useCallback vs useMemo

La diferencia principal entre useCallback y useMemo es lo que cada uno memoiza. useCallback memoiza una función: useCallback(fn, deps) devuelve fn (la misma o la versión anterior). useMemo memoiza el resultado de llamar a una función: useMemo(() => computeExpensive(a, b), [a, b]) devuelve el valor calculado, no una función.

Técnicamente, useCallback es azúcar sintáctico sobre useMemo: useCallback(fn, deps) es equivalente a useMemo(() => fn, deps). Esta sintaxis existe solo para legibilidad — para que el desarrollador vea claramente que se está memoizando una función, no un valor. No hay diferencia de rendimiento entre useCallback y useMemo con una función — generan código idéntico.

HookMemoizaSintaxisUso
useCallbackFunción (referencia)useCallback(fn, deps)Callbacks para componentes hijos
useMemoResultado de cálculouseMemo(() => value, deps)Cálculos costosos, memoización de objetos
jsx
// Estos son equivalentes:
const handleClick = useCallback(() => doSomething(a, b), [a, b]);
const handleClick = useMemo(() => () => doSomething(a, b), [a, b]);

Errores típicos con useCallback

El error más común es envolver sin sentido todas las funciones en useCallback sin React.memo en los componentes hijos. Si un componente hijo no está envuelto en React.memo, se rerenderizará igualmente en cada renderizado del padre, independientemente de si la referencia al callback cambia o no. useCallback sin React.memo es coste sin beneficio.

  • useCallback sin React.memo — el componente hijo igualmente se rerenderiza; la optimización no tiene sentido.
  • Dependencias olvidadas — si una variable usada dentro del callback no está en deps, el callback contiene un stale closure.
  • Memoización excesiva — envolver cada función, incluyendo onClick triviales con console.log.
  • Objetos y arrays en dependencias — una nueva referencia en cada renderizado se considera un cambio, la memoización es inútil.
jsx
// ❌ Inútil: sin React.memo en el hijo
const handle = useCallback(() => doStuff(), []);
<Child onClick={handle} />; // Child aún se rerenderiza sin React.memo

// ❌ Stale closure: falta dependencia
const handle = useCallback(() => {
    console.log(count); // count siempre es 0 — ¡stale closure!
}, []);

// ✅ Correcto: incluir dependencias
const handle = useCallback(() => {
    console.log(count);
}, [count]);

El problema de stale closure en useCallback se soluciona incluyendo todas las variables utilizadas en el array de dependencias. eslint-plugin-react-hooks con exhaustive-deps verifica automáticamente que todas las variables del cuerpo del callback estén presentes en el array. Si el callback usa setState, que no cambia entre renderizados, se puede incluir de forma segura en deps — React garantiza la estabilidad de setState.

Preguntas frecuentes

¿Hay que envolver cada función en useCallback?

No. useCallback solo tiene sentido en tres casos: el componente hijo está envuelto en React.memo, la función se usa en el array de dependencias de useEffect, o la función se pasa a un hook personalizado que depende de la igualdad de referencia. En el resto de casos, useCallback añade sobrecarga sin beneficio. El equipo de React recomienda escribir primero sin optimizaciones y añadirlas según los resultados del perfilado.

¿Qué es más rápido — useCallback o una nueva función en cada renderizado?

Para componentes simples — una nueva función en cada renderizado es ligeramente más rápida, ya que useCallback gasta recursos en comparar dependencias y asignar el array. Para componentes con árboles profundos de React.memo, useCallback gana al prevenir el rerenderizado de miles de elementos hijos. Mide y compara en lugar de adivinar — usa React DevTools Profiler para una evaluación objetiva.

¿Se puede usar useCallback con funciones async?

Sí, useCallback funciona con funciones async exactamente igual que con las síncronas. El hook memoiza la función en sí misma, y el resultado (una Promise) se devuelve cada vez que se llama. Una función async dentro de useCallback es un patrón común para callbacks estables de carga de datos usados en useEffect: const fetchData = useCallback(async (id) => {...}, []).

¿Cómo depurar problemas con useCallback y React.memo?

Usa React DevTools Profiler — muestra qué componentes se rerenderizan y por qué. Para verificación programática, añade console.log o usa useWhyDidYouUpdate — una librería que registra la causa del rerenderizado. Las causas principales: cambió una prop (incluyendo la referencia del callback), cambió el estado o el contexto. Si useCallback no ayuda — verifica que todas las dependencias estén correctamente especificadas.

¿Cómo pasar useCallback a un componente hijo sin React.memo?

Incluso sin React.memo, useCallback puede ser útil en combinación con useMemo para valores de contexto. Si pasas un objeto con funciones a Context.Provider, envuelve la creación del objeto en useMemo, y cada función en useCallback. Esto evita el rerenderizado de todos los consumidores del contexto cuando cambia una de las funciones. Pero para pasar callbacks directamente en props sin React.memo, no hay beneficio de useCallback.

Resumen

  • useCallback — un hook para memoizar una función, devolviendo una referencia estable hasta que las dependencias cambien.
  • Sinergia con React.memo — useCallback solo es efectivo con React.memo en componentes hijos.
  • Comparación de dependencias — mediante Object.is; objetos/arrays en deps rompen la memoización.
  • Mide, no adivines — añade useCallback solo después de detectar un cuello de botella con el perfilador.
  • Closure estable — incluye todas las variables usadas en el array de dependencias, si no — stale closure.
  • useCallback vs useMemo — useCallback memoiza una función, useMemo memoiza el resultado de un cálculo.
  • Exceso — más del 90% de los casos de uso de useCallback en proyectos reales es optimización prematura.

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también