Saltar al contenido

Tracking automático de dependencias

Cómo un efecto descubre solo de qué depende: leer un signal dentro del scope reactivo crea la suscripción. El mecanismo del listener, las dependencias que cambian en cada run y las lecturas que no trackean.

Leer es suscribirse

Un signal en Solid es una función. No es azar sintáctico: la llamada n() es el evento observable. Cuando un efecto está corriendo, Solid mantiene una referencia global al listener actual —la computación en curso—.

Cada vez que se invoca el getter de un signal, ese getter mira quién es el listener y, si hay uno, teje un enlace bidireccional: el signal apunta al efecto como observador, y el efecto apunta al signal como fuente.

TSX
createEffect(() => {  // mientras corre este cuerpo, el listener global = este efecto  console.log(a(), b()); // a() y b() registran la suscripcion al leerse});

Ese enlace se teje en tiempo de ejecución, en el instante de la lectura. No hay análisis estático, no hay compilador mirando qué nombres aparecen: es puramente dinámico. La consecuencia es exacta: el efecto depende de aquello que de hecho leyó, ni más ni menos.

El mecanismo se propaga a través de los memos sin que hagas nada. Si un efecto lee un memo y el memo lee dos signals, el efecto queda atado a esos dos signals de forma transitiva: cuando cualquiera cambie, el memo se recomputa y, si su valor cambió, el efecto reacciona.

TSX
const [precio, setPrecio] = createSignal(100);const [iva, setIva] = createSignal(0.21);const total = createMemo(() => precio() * (1 + iva()));createEffect(() => console.log("total", total())); // reacciona a precio e iva

Dependencias dinámicas

Antes de cada ejecución, Solid borra las suscripciones anteriores del efecto y las vuelve a recolectar desde cero durante el nuevo run. Las dependencias no son fijas: cambian de una ejecución a otra según qué ramas se tomen.

TSX
const [mostrar, setMostrar] = createSignal(true);const [detalle, setDetalle] = createSignal("hola");createEffect(() => {  if (mostrar()) {    console.log(detalle());  } else {    console.log("oculto");  }});

Cuando mostrar es true, el efecto lee detalle y queda suscrito a ambos. En el momento en que mostrar pasa a false, el efecto vuelve a correr, ya no entra en la primera rama, no lee detalle, y se desuscribe de él.

A partir de ahí, cambiar detalle no dispara nada hasta que mostrar vuelva a ser true. Las dependencias siguen al flujo de control real, no al texto del programa. Esto es imposible de expresar con una lista estática, y es una de las razones por las que el tracking automático borra toda una familia de bugs.

Lecturas que no trackean

La regla tiene un reverso igual de importante: solo trackean las lecturas que ocurren de forma síncrona dentro del cuerpo del efecto, mientras el listener está activo. Hay tres situaciones donde lees un signal y aun así no te suscribes.

La primera: leer fuera de todo scope reactivo. Un n() en el nivel superior de un módulo, sin efecto ni memo alrededor, devuelve el valor pero no suscribe a nadie, porque no hay listener.

La segunda: leer dentro de un callback asíncrono. Cuando el efecto arranca un setTimeout o un then de promesa, ese callback corre más tarde, cuando el efecto ya terminó y el listener ya no existe.

TSX
createEffect(() => {  const id = usuarioId();   // SI trackea: lectura sincrona  fetch(`/api/${id}`).then(() => {    console.log(pagina());  // NO trackea: el listener ya se fue  });});

La tercera: leer bajo untrack. A veces quieres el valor de un signal sin depender de él —consultarlo sin que sus cambios te reactiven—. untrack(fn) ejecuta fn con el listener temporalmente apagado, así que las lecturas de dentro no suscriben.

TSX
import { untrack } from "solid-js";createEffect(() => {  const q = consulta();                 // dependencia real  const t = untrack(() => tema());      // se lee, pero no reactiva  registrar(q, t);});

Aquí el efecto reacciona a consulta pero no a tema: cuando cambie el tema, el efecto no correrá, aunque su último valor sí se usó. Es la herramienta para decir “quiero el dato, no la dependencia”.