wandres.dev
CREATEMEMO · valores derivados

Cuándo NO necesitas un memo

En Solid, una función que lee signals ya es reactiva: no hace falta un memo para 'activarla'. Sobre-memoizar llena el grafo de nodos inútiles. La disciplina profesional es derivar con funciones por defecto y reservar createMemo para cuando hay una razón concreta.

⏱ 12 min

Quien llega de React arrastra un reflejo: envolver cada valor derivado en un memo por si acaso. En Solid ese reflejo es un error. Aquí una función que lee signals ya es reactiva —no necesita un createMemo que la “encienda”—. Memoizar de más no es prudente: es llenar el grafo de nodos que consumen memoria y no ahorran nada. Esta lección cierra el nivel con la disciplina que separa un grafo elegante de uno hinchado.

🎯 Al terminar esta lección sabrás
  • Entender por qué una función que lee signals ya es reactiva.
  • Ver qué de React deja de tener sentido en Solid.
  • Reconocer los casos donde un memo no aporta nada.
  • Adoptar la regla: por defecto función, memo con motivo.

La función ya es reactiva

En Solid, el componente corre una sola vez; las funciones que defines dentro tienen identidad estable para siempre, y una función que lee signals propaga reactividad al ámbito que la llama, sin nodo intermedio.

const [precio, setPrecio] = createSignal(100);
const [iva] = createSignal(0.21);

// Reactiva de por sí. No necesita memo para funcionar:
const conIva = () => precio() * (1 + iva());

return <p>Total: {conIva()}</p>; // se actualiza cuando precio cambia

conIva no es “menos reactiva” que un memo: cuando precio cambia, el JSX que la lee se recomputa igual. La única diferencia sería el cacheo, y aquí, con un lector y una multiplicación, cachear no ahorra absolutamente nada. Un createMemo sobre esto es un nodo de más sin contrapartida: más memoria, más grafo, cero beneficio.

Y una función derivada es un ciudadano de primera: puedes pasarla como prop igual que un signal, porque ambas son Accessor. El hijo la lee y se suscribe, sin saber si detrás hay un signal, un memo o una fórmula.

const conIva = () => precio() * (1 + iva());

<Factura total={conIva} />; // el hijo hace total() y reacciona a precio

La excepción idiomática es children: cuando recibes hijos y necesitas manipularlos —contarlos, envolverlos, leer sus props—, el helper children() sí memoiza y resuelve su reactividad por ti. No es sobre-memoizar: es la herramienta correcta para un caso donde el resultado se lee varias veces y debe resolverse una.

Fija la regla de oro del nivel: en Solid, “reactivo” es el estado por defecto de cualquier función que lea signals. El memo no aporta reactividad —eso ya lo tienes gratis—, aporta caché e identidad. Confundir ambas cosas es el origen de casi toda la sobre-memoización.

Lo que en React memoizabas, aquí no

useMemo y useCallback existen porque el componente de React se re-ejecuta entero en cada render: sin memoizar, cada valor y cada función se recrean, y las listas de dependencias son el precio de estabilizarlos. Nada de eso aplica en Solid.

// React: memoizas para sobrevivir al re-render
const total = useMemo(() => precio * (1 + iva), [precio, iva]);
const onClick = useCallback(() => enviar(id), [id]);

// Solid: el componente corre una vez; nada de esto hace falta
const total = () => precio() * (1 + iva());
const onClick = () => enviar(id()); // identidad ya estable

En Solid no hay listas de dependencias que mantener ni useCallback para estabilizar funciones. Y desaparece un riesgo sutil pero caro: la closure obsoleta. En React, una función definida en un render captura los valores de ese render; si la usas más tarde, ve datos viejos, y por eso existen las listas de dependencias. Aquí no hay render que capturar: tu función lee accesores en el momento de ejecutarse, así que siempre ve el valor actual. Traer el reflejo de memoizar todo desde React no te hace más cuidadoso; te hace construir un grafo mayor del necesario.

Los casos donde el memo sobra

🪶

Cálculo trivial

Aritmética, concatenar cadenas, leer una propiedad. Recomputar cuesta menos que mantener el nodo. props.nombre ya es reactivo: no lo envuelvas.

1️⃣

Un solo lector

Sin varios consumidores que compartan el resultado en el mismo tick, el memo no deduplica nada: el único lector recomputaría igual. El cacheo no tiene a quién servir.

🧩

Ya dentro de un tracking

Un valor que solo usas en una expresión del JSX o dentro de un único efecto no gana nada por vivir en su propio nodo.

Hay además un reflejo específico que conviene desactivar: memoizar el acceso a props. En Solid props es un proxy reactivo, así que props.total ya rastrea:

const total = createMemo(() => props.total); // ❌ nodo inútil
<p>{props.total}</p>                          // ✅ ya es reactivo

Y destructurar props sí rompe la reactividad, pero la solución es no destructurar —leer props.total allí donde lo necesites—, no memoizar por encima para tapar el destrozo. El memo no arregla una lectura mal hecha; solo la esconde tras un nodo más.

El coste de un memo trivial no se aprecia en un microbenchmark, pero se acumula: multiplícalo por cientos de derivaciones en una aplicación grande y tienes un grafo que tarda más en montarse y en propagarse por pura hojarasca. La sobriedad no es estética; es rendimiento agregado.

flowchart TD
Q[necesito derivar un valor] --> C[caro y con varios lectores]
Q --> P[corta propagacion con equals]
Q --> R[referencia estable aguas abajo]
C -->|no| F[usa una funcion]
P -->|no| F
R -->|no| F
C -->|si| M[usa createMemo]
P -->|si| M
R -->|si| M
style F fill:#a6e3a1,color:#11111b
style M fill:#cba6f7,color:#11111b
style Q fill:#89b4fa,color:#11111b

Las tres razones legítimas

Un memo se justifica cuando concurre al menos una de estas —y entonces sí, con firmeza—:

  1. Cálculo caro compartido: derivar pesa y varios consumidores lo leen en el mismo tick. El cacheo convierte N recomputaciones en una.
  2. Corte de propagación: quieres que equals detenga la cadena cuando el resultado no cambia, como viste en la lección 4.
  3. Identidad estable: un For, un children o un efecto aguas abajo necesitan que la referencia no cambie salvo que el contenido cambie de verdad.
// Razón 1 en estado puro: caro + compartido -> memo
const filtrados = createMemo(() => productos().filter(coincide).sort(porPrecio));
// lo leen la lista, el contador de resultados y el resumen: un cálculo, tres lectores

Fuera de estos tres motivos, la función simple gana en legibilidad, en memoria y en tamaño del grafo. La duda —“¿pongo memo?”— se resuelve no con un reflejo sino con una pregunta: “¿cuál de las tres razones se cumple?”. Si ninguna, no lo pongas.

Y si dudas entre función y memo sin poder nombrar la razón, la respuesta es función. Lo simple es el valor por defecto; lo complejo se justifica con un motivo explícito, nunca al revés. Esa asimetría —justificar la complejidad, no la simplicidad— es el hábito que mantiene sanos los grafos grandes.

Función por defecto

Toda derivación empieza siendo una función. Es barata, legible y ya reactiva. No pruebes lo contrario: pruébate a ti mismo que necesitas más.

🧠

Memo con motivo

Asciendes a memo solo cuando nombras la razón: caro y compartido, corte de propagación, o identidad estable. Una de las tres, o nada.

🚫

Nunca signal a mano

Derivar hacia un signal sincronizado con un efecto duplica la verdad. No es una tercera opción: es un error con dos disfraces.

📝
Medir antes que memoizar

Ante la duda, no memoices por si: mide. Solid rinde de sobra sin memos en la enorme mayoría de derivaciones, y un perfil real —cuántas veces corre de verdad tu cálculo, cuánto tarda— resuelve la discusión mejor que cualquier intuición. Añade el createMemo cuando el perfil o una de las tres razones lo pida, no antes. Optimizar sin medir es, en Solid como en cualquier sitio, decorar el código con complejidad que aparenta rigor sin aportarlo.

El grafo no es gratis: la elegancia es minimizarlo

El sello de un ingeniero de Solid maduro no es cuántos memos usa, sino cuán pocos y cuán intencionados son. Cada nodo que añades es memoria viva, una suscripción que mantener y una arista más que el sistema recorre en cada propagación. Un grafo hinchado de memos triviales no es “más optimizado”: es más grande, más lento de recorrer y más difícil de razonar, con el agravante de que aparenta rigor cuando solo hay ruido. La reactividad de grano fino de Solid ya te da lo que en React tenías que fabricar a mano: identidad estable por defecto, reactividad automática en cualquier función que lea signals, y tracking exacto sin listas de dependencias. Aprovéchalo confiando en la función simple como forma base de toda derivación, y ascendiendo a createMemo únicamente cuando puedas nombrar la razón. Ese es el cierre del nivel: no memoizar más, sino memoizar justo. El mejor memo es, muchas veces, el que decides no escribir.

⚔️ Poda tu grafo
  1. Revisa un componente tuyo y lista cada createMemo. Para cada uno, nombra cuál de las tres razones lo justifica.
  2. Encuentra un memo que no encaje en ninguna y degrádalo a función simple. Comprueba que todo sigue reactivo.
  3. Busca un createMemo(() => props.algo) y elimínalo: usa props.algo directo. Verifica que sigue rastreando.
  4. Localiza una derivación cara compartida por varios lectores que no sea memo y promuévela. Nombra la razón exacta.
  5. Pasa una función derivada como prop a un hijo y confirma que reacciona sin que hubiera ningún memo de por medio.