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.
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 cambiaconIva 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 precioLa 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-renderconst total = useMemo(() => precio * (1 + iva), [precio, iva]);const onClick = useCallback(() => enviar(id), [id]);// Solid: el componente corre una vez; nada de esto hace faltaconst total = () => precio() * (1 + iva());const onClick = () => enviar(id()); // identidad ya estableEn 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
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); // MAL: nodo inútil<p>{props.total}</p> // BIEN: ya es reactivoY 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.
Las tres razones legítimas
Un memo se justifica cuando concurre al menos una de estas —y entonces sí, con firmeza—:
- Cálculo caro compartido: derivar pesa y varios consumidores lo leen en el mismo tick. El cacheo convierte N recomputaciones en una.
- Corte de propagación: quieres que
equalsdetenga la cadena cuando el resultado no cambia, como viste en la lección 4. - Identidad estable: un
For, unchildreno 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 -> memoconst filtrados = createMemo(() => productos().filter(coincide).sort(porPrecio));// lo leen la lista, el contador de resultados y el resumen: un cálculo, tres lectoresFuera 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.