Saltar al contenido

El compilador de React y el futuro de memo

Si el compilador memoiza por mí, ¿para qué aprendí todo lo anterior?

Al terminar vas a poder explicar

  • Explicar qué transformación hace el compilador sobre tu componente
  • Decir qué código puede memoizar y cuál no, y por qué
  • Decidir qué hacer con los useMemo y useCallback que ya tenés escritos

Si llegaste hasta acá, la pregunta es inevitable: si el compilador memoiza por mí, ¿para qué aprendí todo esto?

La respuesta corta: porque el compilador sólo puede hacer su trabajo si tu código respeta reglas que ahora entendés por qué existen. Y cuando no puede, necesitás saber leer por qué.

Qué hace, en concreto

El compilador de React analiza tus componentes en tiempo de build y inserta la memoización que vos escribirías a mano. Pero con una granularidad que a mano sería insoportable.

lo que escribís
1function Lista({ items, filtro }) {2  const visibles = items.filter(i => i.tipo === filtro);3  const alSeleccionar = (id) => console.log(id);4 5  return <Tabla filas={visibles} onSelect={alSeleccionar} />;6}
lo que produce, en esencia
1function Lista({ items, filtro }) {2  const $ = useMemoCache(4);              // una caché por componente3 4  let visibles;5  if ($[0] !== items || $[1] !== filtro) {6    visibles = items.filter(i => i.tipo === filtro);7    $[0] = items; $[1] = filtro; $[2] = visibles;8  } else {9    visibles = $[2];                       // se reutiliza10  }11 12  let alSeleccionar;13  if ($[3] === Symbol.for("vacio")) {14    alSeleccionar = (id) => console.log(id);15    $[3] = alSeleccionar;16  } else {17    alSeleccionar = $[3];18  }19 20  return <Tabla filas={visibles} onSelect={alSeleccionar} />;21}

Reconocés la idea: es useMemo y useCallback, pero por expresión en vez de por bloque, con las dependencias calculadas exactamente. Nunca una de más, nunca una de menos.

Cuándo se rinde

El compilador es conservador a propósito: si no puede probar que una transformación es segura, deja el componente como está. Y avisa.

  • Mutación durante el render. Si modificás un objeto que venía de props o de un render anterior, no puede razonar sobre las dependencias.
  • Hooks condicionales. Si la cadena puede cambiar de forma, la caché por posición deja de ser válida.
  • Efectos secundarios en el cuerpo. Si tu componente no es puro, memoizar cambiaría el comportamiento.
  • Refs leídas durante el render. Su valor no es parte del grafo de datos que el compilador puede seguir.

Esa lista es, palabra por palabra, las Reglas de React. No eran burocracia: eran el contrato que hace posible el análisis estático.

Qué hacer con lo que ya tenés escrito

1// Antes                            Con el compilador2─────────────────────────────    ─────────────────────────────3useMemo(() => calc(a), [a])      calc(a)          ← se puede sacar4useCallback(fn, [x])             fnse puede sacar5memo(Componente)                 Componentese puede sacar6 7// Lo que NO se saca:8useMemo(() => new WebSocket(u), [u])   // identidad semántica, no rendimiento9useMemo(() => caro(x), [x])            // si el cálculo es caro EN SÍ

La distinción es la misma que vimos en la lección de useMemo: memoizar por identidad contra memoizar por costo. El compilador se ocupa de la identidad. El costo sigue siendo tuyo — y si el cálculo es caro, el useMemo se queda.

Lo que el compilador no hace

  • No arregla arquitectura. Si el estado está diez niveles más arriba de donde se usa, el compilador memoiza el desastre en vez de resolverlo.
  • No sabe cuánto cuesta tu código. Memoiza por identidad, no por costo.
  • No adivina intenciones. Un efecto que sobra sigue sobrando.
  • No es gratis. La caché ocupa memoria; en componentes triviales el envoltorio puede costar más que el render.

Antes de seguir · predecí

El compilador salta un componente y avisa que no pudo optimizarlo. ¿Qué significa?

Lo que te llevás, de todo el curso

El compilador automatiza la memoización, no el criterio. Lo que no automatiza es saber qué pasa cuando algo no funciona — y eso es lo que estuviste construyendo en estas treinta lecciones.

Ahora, cuando un componente se comporte raro, no vas a probar cosas hasta que ande. Vas a poder preguntarte en qué fase estás, qué hay en la cadena de hooks, qué lane se está procesando y por qué ese Fiber no hizo bailout. Esa diferencia no caduca cuando sale la versión que viene.

Antes de marcarla, comprobá

  • ¿Podés explicar explicar qué transformación hace el compilador sobre tu componente?
  • ¿Podés explicar decir qué código puede memoizar y cuál no, y por qué?
  • ¿Podés explicar decidir qué hacer con los useMemo y useCallback que ya tenés escritos?