Abusar de React.memo y useMemo es una señal de alerta
useMemo en todas partes no está haciendo tu app más rápida.
En algún punto una regla de oro se volvió costumbre: envolver cada componente en React.memo, cada valor derivado en useMemo, cada callback en useCallback, por si acaso. Parece trabajo de rendimiento. En su mayoría es ruido. La memoización no es gratis, y aplicarla a ciegas puede volver la app más lenta y sin duda más difícil de leer. Abusar de memo es tanta señal de alerta como no usarlo nunca.
La memoización tiene un costo
Cada memo hace contabilidad para poder saltarse trabajo después:
useMemo/useCallbackguardan el arreglo de dependencias y el valor cacheado, y luego comparan las deps en cada render. Si el cálculo era barato, reemplazaste una operación barata por una operación barata más una asignación y una comparación. Eso puede ser una pérdida neta.React.memocorre una comparación de props antes de cada render potencial. Si alguna prop es un objeto, arreglo o función inline nuevo en cada render, la comparación siempre falla y pagas por ella sin ganar nada.
Así que el modelo mental "memo = más rápido" es incorrecto. Memo intercambia trabajo de comparación ahora para evitar trabajo de recálculo después. Solo vale la pena cuando el trabajo evitado es genuinamente caro y de verdad se evita.
Mide primero, luego memoiza
El orden correcto es medir y luego optimizar:
- Perfila. Usa el React Profiler (o el flamegraph de las DevTools) para encontrar los componentes que de verdad se re-renderizan seguido y cuestan tiempo real. Graba una interacción que se sienta lenta.
- Encuentra el cuello de botella real. Suele ser un número pequeño de componentes o un cálculo caro, no todo el árbol.
- Memoiza solo eso. Aplica
useMemoal cálculo genuinamente caro,React.memoal componente que se re-renderiza sin necesidad con props estables. Deja el resto en paz.
// Señal de alerta: memoizar un valor trivial "por si acaso"
const label = useMemo(() => `${first} ${last}`, [first, last]); // inútil
// Justificado: el profiler mostró que este sort domina el tiempo de render
const sorted = useMemo(
() => bigList.slice().sort(expensiveComparator), // costo real, evitado
[bigList]
);
Cómo se ve lo correcto
| Hábito | Señal |
|---|---|
| Memoizar todo por defecto | Alerta: complejidad agregada, costo de comparación, sin ganancia medida |
| Memoizar lo que un perfil demostró lento | Verde: dirigido, defendible, aún legible |
| Recurrir a memo antes de perfilar | Alerta: optimizar a ciegas |
El beneficio de medir primero es menos ruido, ganancias reales y código que se mantiene legible. Un memo que puedes justificar señalando un flamegraph vale la pena. Uno que agregaste por superstición es solo deuda técnica que parece diligencia.
Movidas avanzadas
- Deja que el profiler nomine candidatos. Nunca memoices un componente que no hayas visto portarse mal en una grabación.
- Arregla la causa, no el síntoma. A menudo el problema real es una prop inestable creada en el padre; estabilízala una vez en lugar de envolver cada hijo en
React.memo. - Recuerda el compilador. El React Compiler auto-memoiza en tiempo de build, lo que vuelve en gran medida redundantes los
useMemo/useCallbackhechos a mano de aquí en adelante. La memoización manual es cada vez más un olor, no una insignia. - Borra los memos injustificados en el review. Si nadie puede nombrar la medición que motivó un memo, quítalo. La legibilidad es una función de rendimiento para el equipo.
Recursos
- Documentación de React —
useMemo("¿Deberías agregar useMemo en todas partes?") - Documentación de React —
React.memo - Documentación de React — Profiler y medición de rendimiento
- React Compiler — memoización automática
¿Desarrollas sistemas de IA o en la nube? Yeda AI audita y refuerza pipelines de LLM y de nube en producción. Hablemos · Lee el blog