Hook useMemo
Cachea el resultado de un cálculo costoso entre renderizados.
Receta
Tarjeta de referencia rápida — lista para copiar y pegar.
const memoizedValue = useMemo(() => computeExpensive(a, b), [a, b]);
// Memoiza un array/objeto derivado para estabilizar referencias
const filtered = useMemo(
() => items.filter((item) => item.active),
[items]
);Cuándo usarlo: Tienes un cálculo costoso que no debe volver a ejecutarse en cada renderizado, o necesitas una referencia estable de objeto/array para un array de dependencias o prop de hijo.
Ejemplo Funcional
"use client";
import { useMemo, useState } from "react";
function fibonacci(n: number): number {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
export function FibonacciCalculator() {
const [num, setNum] = useState(10);
const [theme, setTheme] = useState<"light" | "dark">("light");
const result = useMemo(() => fibonacci(num), [num]);
return (
<div className={theme === "dark" ? "bg-gray-900 text-white p-4" : "bg-white text-black p-4"}>
<label className="flex items-center gap-2">
n =
<input
type="number"
value={num}
onChange={(e) => setNum(Number(e.target.value))}
className="w-20 border rounded px-2 py-1"
max={40}
/>
</label>
<p className="mt-2 font-mono">fibonacci({num}) = {result}</p>
<button
onClick={() => setTheme((t) => (t === "light" ? "dark" : "light"))}
className="mt-2 px-3 py-1 border rounded text-sm"
>
Cambiar tema
</button>
</div>
);
}Lo que esto demuestra:
- El cálculo costoso
fibonaccies memoizado y solo se recalcula cuandonumcambia - Cambiar el tema no recomputa el resultado de fibonacci
- Sin
useMemo, cada re-renderizado (incluyendo cambio de tema) volvería a ejecutar el cálculo costoso
Profundización
Cómo funciona
- En el primer renderizado, React llama tu función factory y almacena el resultado
- En renderizados subsecuentes, React compara cada dependencia con su valor anterior usando
Object.is - Si todas las dependencias son iguales, React devuelve el resultado en caché sin llamar la factory
- Si alguna dependencia cambió, React llama la factory nuevamente y cachea el nuevo resultado
useMemoes una optimización de rendimiento, no una garantía semántica — React puede descartar el caché en algunos casos (p. ej., componentes fuera de pantalla)
Parámetros y Valores de Retorno
| Parámetro | Tipo | Descripción |
|---|---|---|
factory | () => T | Función que calcula el valor a memoizar |
dependencies | unknown[] | Array de valores reactivos de los que depende el cálculo |
| Retorno | Tipo | Descripción |
|---|---|---|
memoizedValue | T | Resultado en caché de la función factory |
Variaciones
Estabiliza un objeto para un array de dependencias:
const options = useMemo(
() => ({ page, pageSize, sortBy }),
[page, pageSize, sortBy]
);
useEffect(() => {
fetchData(options);
}, [options]);Memoiza una lista filtrada/ordenada:
const sortedUsers = useMemo(
() => [...users].sort((a, b) => a.name.localeCompare(b.name)),
[users]
);Memoiza un árbol de componentes (raro):
const chart = useMemo(
() => <ExpensiveChart data={data} />,
[data]
);Notas de TypeScript
// El tipo de retorno se infiere de la factory
const total = useMemo(() => items.reduce((sum, i) => sum + i.price, 0), [items]);
// total: number
// Genérico explícito cuando la inferencia es muy amplia
const config = useMemo<Config>(() => ({ retries: 3, timeout: 5000 }), []);Atrapadas
-
Optimización prematura — Envolver cada valor en
useMemoañade complejidad sin beneficio medible para cálculos baratos. Solución: Perfila primero. Solo memoiza cuando puedas medir un problema de rendimiento. -
Dependencias faltantes — Omitir una dependencia causa que el valor memoizado sea obsoleto. Solución: Incluye todos los valores reactivos usados dentro de la factory.
-
Referencias de dependencias inestables — Si una dependencia es un objeto/array nuevo en cada renderizado,
useMemorecalcula de todas formas. Solución: Memoiza la dependencia misma, o reestructura para depender de primitivos. -
Efectos secundarios en la factory —
useMemose ejecuta durante el renderizado; los efectos secundarios (fetching, subscripciones) rompen las reglas de React. Solución: Mueve los efectos secundarios auseEffecto manejadores de eventos. -
No es una garantía — React puede descartar valores memoizados para componentes fuera de pantalla y recalcular más tarde. Solución: Nunca confíes en
useMemopara la corrección — solo para el rendimiento.
Alternativas
| Alternativa | Úsalo cuando | No lo uses cuando |
|---|---|---|
| Sin memoización | El cálculo es barato (concatenación de strings, matemática simple) | El cálculo es notablemente lento |
React.memo | Quieres omitir el re-renderizado de un componente hijo completo | Necesitas memoizar un valor, no un componente |
useCallback | Necesitas una referencia de función estable, no un valor estable | Necesitas cachear un valor no función |
useDeferredValue | Quieres diferir el renderizado de contenido costoso sin bloquear entrada | Necesitas cachear resultados de cálculo |
| Cálculo del lado del servidor | El trabajo costoso puede ocurrir en tiempo de construcción o solicitud | Los datos dependen del estado del lado del cliente |
¿Cuándo vale la pena? Como regla general, memoiza cuando el cálculo toma más de ~1ms o cuando el resultado se usa como dependencia para useEffect, useCallback, o se pasa a un hijo React.memo.
Preguntas Frecuentes
¿Cuándo debo usar realmente useMemo vs simplemente calcular el valor en línea?
- Usa
useMemocuando el cálculo toma más de ~1ms (perfila primero). - También úsalo cuando el resultado se usa como dependencia para
useEffect,useCallback, o se pasa a un hijoReact.memo. - Para operaciones baratas como concatenación de strings o matemática simple, omite
useMemo.
¿Es useMemo una garantía de que mi función nunca se ejecutará nuevamente con las mismas deps?
- No. React puede descartar valores memoizados para componentes fuera de pantalla y recalcular más tarde.
useMemoes una optimización de rendimiento, no una garantía semántica.- Nunca confíes en esto para la corrección -- solo para el rendimiento.
Atrapada: ¿Por qué mi useMemo recalcula en cada renderizado aunque proporcioné dependencias?
- Una de tus dependencias es probablemente una referencia inestable (un objeto o array nuevo creado durante el renderizado).
- React compara dependencias con
Object.is, así que una referencia nueva significa "cambió". - Soluciona memoizando la dependencia misma o reestructurando para depender de primitivos.
¿Cuál es la diferencia entre useMemo y useCallback?
useMemo(() => value, deps)cachea el valor de retorno de la función factory.useCallback(fn, deps)cachea la función misma (equivalente auseMemo(() => fn, deps)).- Usa
useMemopara valores,useCallbackpara funciones.
¿Puedo ejecutar efectos secundarios dentro de useMemo?
- No.
useMemose ejecuta durante el renderizado, así que los efectos secundarios (fetching, subscripciones, mutaciones del DOM) rompen las reglas de React. - Mueve los efectos secundarios a
useEffecto manejadores de eventos. - La función factory debe ser pura.
¿Cómo estabilizo un objeto pasado a dependencias de useEffect usando useMemo?
const options = useMemo(
() => ({ page, pageSize, sortBy }),
[page, pageSize, sortBy]
);
useEffect(() => {
fetchData(options);
}, [options]); // referencia estable, el efecto solo se vuelve a ejecutar cuando los valores cambian¿Cómo tipeo useMemo con un genérico explícito en TypeScript?
interface Config {
retries: number;
timeout: number;
}
const config = useMemo<Config>(
() => ({ retries: 3, timeout: 5000 }),
[]
);
// config: ConfigAtrapada: ¿Envolver cada valor en useMemo mejora el rendimiento?
- No.
useMemotiene sobrecarga — almacenar el valor, comparar dependencias y mantener el caché. - Para cálculos baratos, la sobrecarga de memoización puede exceder el costo del cálculo.
- Perfila primero y solo memoiza donde midas un beneficio de rendimiento real.
¿Puedo usar useMemo para memoizar un árbol de componentes React?
const chart = useMemo(
() => <ExpensiveChart data={data} />,
[data]
);- Esto es válido pero raro. Previene que React re-renderice el subárbol cuando
datano ha cambiado. - Prefiere
React.memoen el componente mismo para la mayoría de casos.
¿Cómo se compara useMemo con calcular valores derivados en un componente del servidor?
- Los componentes del servidor pueden calcular valores costosos en tiempo de solicitud o construcción con cero costo del lado del cliente.
useMemoes para cálculo del lado del cliente que depende del estado del cliente.- Si los datos no dependen de interacciones del cliente, prefiere cálculo del lado del servidor.
¿Qué sucede con el tipo de retorno de TypeScript si no proporciono un genérico explícito?
- TypeScript infiere el tipo de retorno de la función factory automáticamente.
useMemo(() => items.reduce((sum, i) => sum + i.price, 0), [items])infierenumber.- Los genéricos explícitos solo son necesarios cuando la inferencia es demasiado amplia o quieres un tipo más estrecho.
Relacionado
- useCallback —
useMemopara funciones - useEffect — estabiliza dependencias con
useMemopara evitar re-ejecutar efectos - useDeferredValue — difiere el renderizado de contenido costoso
- useRef — almacena valores sin disparar re-renderizados