Web Design and Engineering

El React Compiler ya está aquí: ¿sigue teniendo sentido memoizar a mano?

React Compiler 1.0 llegó estable en octubre de 2025. Aquí sigue teniendo sentido usar useMemo, useCallback y React.memo, y aquí ya no.

23 de abril de 20267 min de lectura
El React Compiler ya está aquí: ¿sigue teniendo sentido memoizar a mano?

Añadiste useMemo para frenar un filtro sobre una lista. Envolviste cada handler en useCallback para que un hijo memoizado dejara de re-renderizar. Subiste la mitad del árbol a React.memo cuando el profiler se veía mal. Funcionó. También duplicó la carga mental de cada componente y convirtió los refactors en algo doloroso.

React Compiler 1.0 salió el 7 de octubre de 2025, tras dos años de release candidate y despliegue en producción dentro de Meta. Se encarga de buena parte de ese trabajo de forma automática. La pregunta ya no es si adoptarlo, sino cuáles de tus llamadas manuales de memoización siguen teniendo sentido. Así lo pensamos en Studio cuando tomamos una base de código React.

Qué hace realmente el compiler

React Compiler es un compilador de optimización para React que se ejecuta en build time. Lee tus componentes y hooks, entiende el flujo de datos y la mutabilidad, e inserta memoización de forma automática. Puede memoizar valores individuales dentro de un componente, memoizar a través de ramas condicionales, y saltarse el re-render de un subárbol cuando los inputs a ese subárbol son estables. No lo importas en tiempo de ejecución. Lo añades al pipeline de build y reescribe el código que se envía.

Es compatible con React 17, 18 y 19. En React 17 y 18 se añade el paquete react-compiler-runtime; en React 19 funciona de forma nativa. En Next.js se activa desde experimental.reactCompiler en next.config.js. Expo, Remix, Vite y Metro tienen todas configuraciones soportadas.

En una base de código que el compiler pueda cubrir, el efecto neto es que la mayoría de lo que hacías a mano con useMemo, useCallback y React.memo pasa a ser trabajo del compilador.

Qué reemplaza por defecto

  • useMemo para valores derivados. Filtrar una lista, ordenarla, calcular totales: el compilador memoiza todo eso automáticamente según los inputs que ve.
  • useCallback para handlers de eventos. Si un handler se pasa a un componente hijo, el compilador estabiliza su referencia para que el hijo no re-renderice.
  • React.memo en la mayoría de componentes. Si las props de un componente ya están estabilizadas aguas arriba, el compilador se salta el re-render de ese componente sin que tengas que envolverlo.

Para código nuevo, la guía oficial del equipo de React es escribir componentes de forma simple y dejar la memoización al compilador. Los hooks manuales solo aparecen cuando necesitas un control preciso.

Dónde la memoización manual todavía importa

La propia documentación del compiler mantiene cuatro vías de escape, no como legado sino como los bordes cortantes que ha decidido no limar.

1. Valores usados como dependencias de un efecto

Cuando un valor se usa dentro del array de dependencias de un useEffect, necesitas control exacto sobre cuándo se considera nuevo. La memoización del compiler es un detalle de implementación: puede cambiar con versiones futuras. Si un efecto debe dispararse solo cuando una derivación concreta cambia, mantén el useMemo para esa derivación. La documentación de React lo lista como motivo de primera clase para conservar memoización manual.

2. Librerías de terceros que comparan por referencia

Librerías de gráficos, tablas, listas virtualizadas: un trozo considerable del ecosistema React compara props por referencia, no por valor. Si D3, AG Grid o TanStack Table esperan que columns sea el mismo array entre renders, tienes que garantizarlo tú. La estabilización del compiler es opaca para ti, así que el patrón más seguro es un useMemo explícito en la frontera donde tu código entrega datos a la librería.

3. Cálculos puros extremadamente costosos

El compiler memoiza de forma agresiva, pero no infinita. Para algo como un cálculo de layout síncrono de 50 ms, quieres ser explícito en que nunca debería ejecutarse dos veces con el mismo input. Envuélvelo en useMemo, deja un comentario explicando el coste, y sigue. La regla es la misma que antes del compiler: envuelve lo que el profiler demuestre costoso, no lo que imaginas que puede serlo.

4. React.memo con función de comparación personalizada

El compiler usa shallow equality por defecto. Si tienes un componente que solo debe re-renderizar cuando cambia un campo específico dentro de un objeto más grande, React.memo(Component, customCompare) sigue siendo la herramienta correcta. El compiler no te va a pelear; simplemente no intentará ser más listo que tu comparador personalizado.

Cómo decidimos en Studio

Sobre una base de código nueva, encendemos el compiler el día uno, activamos el preset recommended-latest de eslint-plugin-react-hooks, y escribimos componentes planos. Añadimos useMemo o useCallback solo cuando se cumple uno de los cuatro casos de arriba, y dejamos un comentario breve indicando cuál. Ese comentario no es decorativo: le dice al siguiente lector por qué este hook en particular sobrevivió y no se puede borrar sin más.

Sobre una base de código existente, no arrancamos la memoización. El equipo de React lo desaconseja explícitamente, porque eliminar un useMemo puede cambiar la salida compilada y sorprenderte. Encendemos el compiler, observamos la salida del build, arreglamos las violaciones de las Rules of React que la capa de validación del compiler reporta, y dejamos vivir las viejas llamadas de memoización. Cuando tocamos un archivo para una feature, limpiamos la memoización de ese archivo como parte del diff. Nunca antes.

Las directivas: 'use memo' y 'use no memo'

El compiler soporta dos directivas a nivel de función para un control localizado.

'use memo' fuerza al compiler a optimizar una función concreta, incluso en una base de código en modo opt-in. Útil cuando adoptas el compiler primero en un subconjunto de componentes.

'use no memo' es lo contrario: le dice al compiler que se salte por completo esta función. Tanto la documentación de React como los propios autores del compiler la plantean como una vía de escape temporal, no como solución permanente. El flujo es: te topas con un bug del compiler, añades 'use no memo' con un TODO, abres un issue, arreglas la violación subyacente, eliminas la directiva.

function Dashboard() {
  'use no memo'
  // TODO(#1234): eliminar cuando arreglemos la mutación dentro de renderRow
  return <Table rows={rows} />
}

No trates 'use no memo' como un opt-out a largo plazo. Si un archivo lo necesita durante meses, algo en ese archivo está violando las Rules of React y el compiler hace bien en apartarse.

Las reglas de lint que reemplazan la vieja costumbre

React Compiler 1.0 llega con un eslint-plugin-react-hooks ampliado. El preset recommended-latest activa un conjunto más amplio de reglas que el compiler deriva de su análisis estático. Las que más cambian el trabajo diario son:

  • purity: señala código impuro en render, incluyendo mutaciones y efectos colaterales que el compiler se niega a memoizar.
  • refs: señala lecturas o escrituras de refs durante el render.
  • set-state-in-effect: señala el clásico antipatrón de llamar setState dentro de un efecto que provoca un bucle de renders derivados.
  • preserve-manual-memoization: avisa cuando un useMemo manual se elimina de una forma que cambiaría el comportamiento, y es la regla que te protege durante una migración incremental.

Si tu equipo era conservador a la hora de activar nuevas reglas de ESLint, activa esta. Se paga sola en el primer refactor.

Qué cambia para los equipos de producto

El resultado interesante no es que las apps van más rápido, aunque vayan más rápido. Es que el código de los componentes se vuelve más corto y más legible. Menos hooks significa menos arrays de dependencias, menos batallas con exhaustive-deps, y menos regresiones de rendimiento introducidas por un desarrollador junior que, sin querer, pasa una referencia inestable.

En los proyectos donde hemos migrado, el beneficio más visible tras tres meses es la velocidad de onboarding. Los nuevos contributors leen el código de arriba abajo en vez de descifrar un árbol de Navidad de wrappers de memoización. El trabajo de rendimiento que sobrevive es el que merece escribirse a mano: el cálculo costoso, la frontera con la librería, la dependencia que un efecto concreto tiene que observar. Todo lo demás es del compilador.

Foto de Shahabudin Ibragimov en Unsplash

Studio

Empieza un proyecto.

Un partner único para empresas, sector público, startups y SaaS. Producción más rápida, tecnología moderna, costes reducidos. Un equipo, una factura.