Memoización
El capítulo de renderizado es el modelo mental en el que se apoya este: cuando el estado cambia, React vuelve a ejecutar el componente y todo lo que está debajo, y eso normalmente está bien porque la mayoría de los renderizados son rápidos. A veces no lo es. Un cálculo al inicio de un componente puede tardar lo suficiente como para notarse, o un subárbol de componentes puede ser lo suficientemente costoso que re-renderizar todos ellos en cada pulsación hace que la aplicación se sienta lenta.
Para esos casos medidos, React ofrece tres herramientas: useMemo, memo y useCallback, y las tres hacen variaciones de una sola cosa: permiten que React recuerde un resultado del renderizado anterior e ignore el trabajo de repetirlo. Para usar cualquiera de ellas bien, primero necesitas saber cómo JavaScript decide si dos cosas son "lo mismo".
Tipos de valor, tipos de referencia e igualdad referencial
JavaScript divide los valores en dos familias. Los tipos de valor (también llamados primitivos) son booleanos, números, strings, y los que encuentras menos frecuentemente: undefined, null, symbol y bigint. Dos de ellos son iguales cuando tienen el mismo valor del mismo tipo: 1 === 1 es verdadero. Los tipos de referencia (también llamados tipos complejos) son objetos, arrays y funciones. Dos de ellos son iguales solo cuando son literalmente la misma cosa en memoria. Verse idénticos no cuenta para nada.
const a = { name: 'María' }
const b = { name: 'María' }
console.log(Object.is(a, b)) // false: dos objetos separados en memoria
const c = a
console.log(Object.is(a, c)) // true: ambos apuntan al mismo objetoObject.is es lo que React usa internamente para estas comparaciones, y se comporta casi idénticamente a === aquí. Incluso {} !== {}: dos literales de objeto vacío son dos asignaciones distintas, así que fallan la comprobación. Esto se llama igualdad referencial, y es el concepto raíz de todo este capítulo.
Ahora conéctalo al renderizado. Un componente es una función, y cada renderizado vuelve a ejecutar su cuerpo de arriba hacia abajo. Cualquier objeto, array o función creado en ese cuerpo es completamente nuevo en cada renderizado:
function App() {
const styles = { backgroundColor: 'black' } // objeto nuevo en cada renderizado
function increment() { /* ... */ } // función nueva en cada renderizado
// ...
}El contenido puede ser idéntico de un renderizado a otro, pero las referencias nunca lo son. Cualquier comparación que pregunte "¿cambió esta prop?" verificando Object.is responderá afirmativamente cada vez. Todo lo que sigue se trata de controlar cuándo cambian esas referencias.
useMemo: salta un cálculo costoso
El primer uso de memoización nada tiene que ver con referencias aún. Un cálculo en el nivel superior de un componente se ejecuta en cada renderizado, incluso los renderizados desencadenados por estado que el cálculo nunca toca. Si ordenar una lista grande de productos toma un tiempo notable, un botón de contador en otro lugar del mismo componente se vuelve entrecortado, porque cada clic vuelve a ejecutar la ordenación sin razón.
useMemo arregla esto con la misma forma que un efecto: una función más un array de dependencias. React llama a la función, recuerda qué devuelve, y en renderizados posteriores devuelve el valor recordado sin llamar a la función de nuevo, mientras nada en el array de dependencias haya cambiado.
import { useMemo, useState } from 'react'
function ProductList({ productsData }) {
const [count, setCount] = useState(0)
const sortedProducts = useMemo(() => {
return [...productsData].sort((a, b) => a.name.localeCompare(b.name))
}, [productsData])
// ...
}El array de dependencias contiene todo lo que el cálculo lee: aquí, solo productsData. Cambiar count re-renderiza el componente, pero React ve que productsData es lo mismo y salta la ordenación completamente. Solo un nuevo array productsData desencadena un recálculo. En la demostración del curso, eso redujo la ordenación de un lag visible en cada clic a cero milisegundos en renderizados que no tocaban los datos.
memo: salta el re-renderizado de un hijo
useMemo almacena en caché un valor dentro de un componente. memo opera un nivel arriba: puede saltar re-renderizar un componente entero. Es un componente de orden superior, una función que toma tu componente y devuelve una versión mejorada del mismo. A menudo lo verás escrito React.memo en codebases más antiguas; la importación moderna tiene nombre.
import { memo } from 'react'
function Product({ name, styles }) {
// un cuerpo de componente costoso
}
export default memo(Product)El componente mejorado compara sus props anteriores con sus props entrantes. Si cada prop es lo mismo, React salta re-renderizar para ese renderizado padre, y el subárbol que hubiera renderizado se salta con él (con excepciones, cubiertas abajo). En la aplicación de demostración del curso, envolver un componente en memo redujo un cambio de estado de treinta y uno a un renderizado, porque todo el subárbol no tocado se saltó en una sola decisión.
"Lo mismo" significa igualdad superficial: cada prop se compara con Object.is. Una prop string o número se compara por valor y se comporta como esperarías. Y es exactamente donde comienza el problema.
Por qué memo silenciosamente deja de funcionar
Pasa a un componente envuelto en memo una prop de objeto, y la optimización muere silenciosamente:
function App() {
const [darkMode, setDarkMode] = useState(false)
const [count, setCount] = useState(0)
const styles = {
backgroundColor: darkMode ? '#222' : '#fff',
color: darkMode ? '#fff' : '#000',
}
return <Product styles={styles} /* ... */ />
}Cada vez que count cambia, App re-renderiza y construye un objeto styles fresco. Los valores dentro son idénticos, pero la igualdad referencial no le importa: Object.is(oldStyles, newStyles) es falso, memo concluye que las props cambiaron, y Product de todas formas se re-renderiza. Nada produce error, nada advierte. La memoización no hace nada en absoluto.
Este es el segundo trabajo de useMemo: mantener una referencia entre renderizados para que una comparación pueda tener éxito. Envuelve la creación del objeto y dale la única dependencia con la que realmente varía:
const styles = useMemo(() => ({
backgroundColor: darkMode ? '#222' : '#fff',
color: darkMode ? '#fff' : '#000',
}), [darkMode])Ahora styles es el mismo objeto, por referencia, en cada renderizado hasta que darkMode cambie. La comprobación de memo pasa, y Product se mantiene saltado cuando solo el contador se mueve. Las dos herramientas se componen: memo en el hijo pregunta "¿cambiaron las props?", y useMemo en el padre asegura que la respuesta pueda ser no.
children es una prop como cualquier otra, y esto atrapa a la gente. Los hijos JSX escritos en el padre son un objeto de elemento nuevo en cada renderizado padre, así que <Card><Chart /></Card> le pasa a Card una nueva referencia children cada vez y memo en Card nunca tiene éxito, sin importar cuán estables sean las otras props. El patrón estructural que renderizado describe funciona del otro lado: cuando el elemento se crea arriba del componente que posee el estado, ese re-renderizado del componente ve el mismo objeto elemento y salta la rama sin ninguna memoización.
Qué memo no salta
memo solo gobierna las props que llegan del padre. Un componente memoizado aún se re-renderiza cuando su propio estado cambia y cuando un contexto que lee se actualiza. El contexto llega más allá de un subárbol saltado también: un descendiente que consume un contexto cambiado se re-renderiza incluso aunque el wrapper memoizado arriba fue saltado.
useCallback: useMemo para funciones
Las funciones son tipos de referencia también, así que una prop de callback rompe memo exactamente de la misma forma. Define function increment() {...} en un cuerpo de componente, pásalo hacia abajo, y el hijo recibe una referencia de función completamente nueva en cada renderizado del padre. useCallback existe precisamente para esto: memoiza la función misma.
const [selectedProduct, setSelectedProduct] = useState(null)
const chooseProduct = useCallback((id) => {
console.log('Nuevo producto seleccionado')
setSelectedProduct(id)
}, [])Pasa chooseProduct hacia abajo a un hijo envuelto en memo y la optimización se mantiene: useCallback(fn, deps) mantiene la misma referencia de función entre renderizados hasta que una dependencia cambie. La relación con useMemo es cercana: con useMemo el valor recordado es lo que tu función devuelve, mientras que con useCallback el valor recordado es la función que pasas.
Un detalle a llevar contigo: funciones de estado setter como setSelectedProduct ya tienen una identidad estable, React lo garantiza. Pasar un setter crudo hacia abajo como una prop es seguro sin useCallback; el hook gana su lugar cuando envuelves el setter en tu propia función que primero hace trabajo extra.
Una referencia de función estable también importa más allá de memo. Si una función aparece en un array de dependencias de useEffect, una referencia nueva en cada renderizado significa que el efecto se re-ejecuta en cada renderizado, como efectos cubre. useCallback también lo quieta.
Mide primero
Confirma que hay un problema de rendimiento antes de buscar cualquiera de esto. Renderizado hace el caso y expone el flujo de trabajo: la mayoría de los renderizados son baratos, un renderizado que no cambia nada nunca llega al DOM, y el Profiler con una CPU limitada es cómo encuentras los que sí cuestan algo. Memoizar un cálculo trivial como data.length cuesta más de lo que ahorra, porque el almacenamiento en caché y la comparación son en sí mismos trabajo. Arregla solo lo que puedes medir, y arregla renderizados lentos antes de gastar energía reduciendo el número de renderizados.
Nota de versión
El Compilador de React, que alcanzó 1.0 en octubre de 2025, automatiza la mayoría de lo que este capítulo cubre: analiza componentes en tiempo de construcción e inserta memoización automáticamente, así que useMemo, useCallback y memo escritos a mano se vuelven en gran medida innecesarios en proyectos que lo adoptan. Los conceptos siguen siendo valiosos de entender, tanto porque los codebases existentes están llenos de estos hooks como porque saber qué hace el compilador por ti hace su comportamiento legible en lugar de mágico.
useMemo permite que React recuerde la respuesta y la reutilice hasta que las entradas cambien. memo hace algo similar para un componente entero: si sus props son lo mismo que la última vez, React salta renderizarlo. La parte complicada es que dos objetos o funciones que se ven idénticos aún cuentan como diferentes en JavaScript, por eso estas herramientas a veces se necesitan entre sí. Y antes de usar cualquiera de ellas, asegúrate que algo es realmente lento.
Lo siguiente: Separación de código, donde la otra palanca, el tamaño del bundle, tiene su turno.

