Saltar al contenido

Lectura reactiva vs no reactiva

El mismo count() se comporta de dos maneras según dónde lo llames: dentro de un ámbito de rastreo se suscribe y reacciona; fuera de él es una lectura inerte. Por qué guardar el valor en una variable congela la reactividad, y por qué en Solid se pasa la función, no el valor.

El mismo count(), dos comportamientos

Un ámbito de rastreo es cualquier lugar donde Solid está ejecutando una computación reactiva y, por tanto, tomando nota de qué signals se leen: el cuerpo de un createEffect, el de un createMemo y las expresiones de tu JSX. Llamar a count() ahí dentro suscribe esa computación al signal. En cambio, llamar a count() en un manejador de evento, en un setTimeout o en una función utilitaria cualquiera es una lectura inerte: te da el valor del momento y no suscribe a nadie.

TSX
const [count, setCount] = createSignal(0);createEffect(() => {  console.log("reactivo:", count()); // se re-ejecuta con cada cambio});function alHacerClic() {  console.log("inerte:", count());   // lee una vez, no re-suscribe  setCount(count() + 1);}

El efecto se volverá a ejecutar cada vez que count cambie, porque su lectura ocurrió dentro de un ámbito de rastreo. El console.log del manejador solo se dispara cuando alguien hace clic: su lectura de count() no crea dependencia alguna.

El pecado de guardar el valor en una variable

Recuerda el axioma de Solid: el cuerpo de un componente se ejecuta una sola vez. De ahí se deduce una trampa mortal. Si en la cima del componente escribes const valor = count(), estás leyendo el signal una vez, en el instante de la creación, y guardando ese número en una variable normal. La variable no es reactiva; es una foto. Cuando count cambie después, valor seguirá anclado a su valor inicial para siempre.

TSX
function Contador() {  const [count, setCount] = createSignal(0);  const valor = count();  // FOTO: se evalua una vez, vale 0 para siempre  return (    <>      <p>Roto: {valor}</p>       {/* nunca cambia */}      <p>Vivo: {count()}</p>     {/* se actualiza con cada setCount */}      <button onClick={() => setCount(count() + 1)}>+1</button>    </>  );}

La regla práctica es incómoda para quien viene de React, donde el componente re-renderiza y const valor = ... se recalcula en cada pasada: en Solid, la llamada al getter debe vivir en el punto exacto del JSX donde se usa el valor. La expresión <p>{count()}</p> funciona porque Solid la re-ejecuta quirúrgicamente; <p>{valor}</p> no, porque valor ya es un número muerto antes de llegar ahí.

Pasar la función, no el valor

La misma lógica gobierna cómo compartes estado entre componentes. Si le pasas a un hijo count(), le entregas un número muerto; el hijo no tiene forma de enterarse de cambios futuros. Si le pasas count —la función—, le das una lectura reactiva que puede invocar dentro de su JSX y suscribirse él mismo.

TSX
// MAL: el hijo recibe un numero congelado<Saludo veces={count()} />// BIEN: el hijo recibe el getter y decide cuando leer<Saludo veces={count} />

Existe también el camino inverso: a veces quieres leer un signal sin suscribirte, incluso dentro de un efecto. Para eso está untrack, que ejecuta una función y descarta cualquier suscripción que ocurra dentro. Es la herramienta para decir “quiero el valor actual de esto, pero no me re-ejecutes cuando cambie”.

TSX
import { untrack } from "solid-js";createEffect(() => {  const a = count();                 // dependencia  const b = untrack(() => other());  // lectura sin dependencia  console.log(a, b);                 // se re-ejecuta con count, no con other});