Saltar al contenido

El árbol de ownership

Cada computación en Solid nace con un dueño: el owner en cuyo contexto se ejecutó. Los owners forman un árbol paralelo al del DOM que gobierna la disposición jerárquica, la limpieza en cascada y el alcance del contexto y los errores.

Qué es un owner

Un owner es un nodo con cuatro piezas y nada más. Guarda una lista de computaciones hijas —lo que se creó dentro de él—, una lista de funciones de limpieza —las que registró onCleanup—, un puntero a su owner padre, y el contexto, los valores que createContext propaga hacia abajo. Esa parquedad es justo lo que permite que la disposición sea determinista y barata.

TypeScript
// forma conceptual del nodo owner en el runtime de Solidinterface Owner {  owned: Computation[] | null;              // computaciones creadas aqui dentro  cleanups: (() => void)[] | null;          // limpiezas registradas con onCleanup  owner: Owner | null;                      // el owner padre  context: Record<symbol, unknown> | null;  // valores de contexto heredados}

Toda computación —createEffect, createMemo, createRenderEffect, createComputed— es a la vez observadora del grafo reactivo y owner: puede tener hijos y limpiezas propias. Un signal, en cambio, no es un owner: es solo un dato observable, una fuente sin descendencia ni ciclo de vida. Por eso hablamos de “el dueño de un efecto”, nunca de “el dueño de un signal”.

Un matiz que conviene fijar pronto: un memo es un híbrido. Como owner posee hijos y limpiezas; como fuente otros pueden leerlo y suscribirse a él. Un createMemo, por tanto, contiene efectos anidados que se disponen con él y a la vez notifica a quienes lo consumen cuando su valor cambia. Un efecto, en cambio, es una hoja del grafo de dependencias —nadie lo lee— aunque sea un nodo interno del árbol de owners con sus propios descendientes.

Cómo se teje el árbol

Solid mantiene una variable interna con el owner actual. Antes de ejecutar el cuerpo de una computación, apunta esa variable a su nodo; cualquier computación creada durante esa ejecución lee la variable y se registra como hija. Al terminar, restaura el owner anterior. Es el mismo mecanismo de pila que gobierna el ámbito léxico de un lenguaje, pero aplicado a la vida de los recursos reactivos.

TSX
import { createEffect, createMemo } from "solid-js";function Panel() {  // owner activo: el del componente Panel  const doble = createMemo(() => base() * 2); // hijo del owner de Panel  createEffect(() => {          // hijo del owner de Panel    createEffect(() => {        // NIETO: hijo del efecto exterior      registrar(doble());    });  });  return <div>{doble()}</div>;}

El efecto interior no es hermano del exterior: es su hijo. Cada nivel de anidamiento del código se corresponde con un nivel del árbol de owners. El render del componente, a su vez, cuelga del owner que lo montó, y así sucesivamente hasta la raíz que abrió render o createRoot.

Este árbol corre en paralelo al del DOM, pero no es idéntico: un componente puede abrir varios owners anidados —uno por cada control de flujo—, y una computación sin salida visual, como un efecto de sincronización, es un nodo del árbol de owners aunque no le corresponda ningún nodo del DOM.

La disposición jerárquica

Cuando un owner se destruye, Solid recorre su subárbol y lo desmonta en profundidad: primero dispone recursivamente a cada hijo y solo después ejecuta sus propias limpiezas. El recorrido va en orden inverso al de creación —LIFO—, de modo que lo último que se montó es lo primero en desmontarse, como al deshacer una pila. Así, un recurso que dependía de otro creado antes se libera primero.

La disposición es síncrona y en profundidad: cuando se dispara, Solid no la difiere ni la reparte en el tiempo. Recorre el subárbol completo de una vez, desconecta cada computación de las fuentes que observaba —para que deje de recibir notificaciones— y ejecuta sus limpiezas. Al volver de ese recorrido, el subárbol entero ha dejado de existir para el sistema reactivo: ni reacciona, ni retiene, ni notifica.

Este barrido es lo que vuelve fiable a onCleanup: no orquestas el desmontaje pieza por pieza. Si un componente sale del DOM —porque un Show cambió de rama o un For perdió un elemento—, Solid dispone el owner de ese subárbol y todas las limpiezas de sus descendientes se ejecutan en cascada, sin intervención tuya.

createRoot es la única forma de abrir una raíz a mano: crea un owner sin padre —una frontera de disposición— y te entrega una función dispose para cerrarlo cuando quieras. Todo lo demás cuelga, directa o indirectamente, de una raíz creada por render o por createRoot.

TSX
import { createRoot, createEffect } from "solid-js";const dispose = createRoot((dispose) => {  createEffect(() => sincronizar(estado()));  return dispose;               // controlas tu la destruccion del ambito});// mas tarde, cuando ya no lo necesites:dispose();                      // corre todas las limpiezas del subarbol

Contexto y errores suben por el árbol

El árbol de owners no solo gobierna la disposición: es también el canal por el que viajan el contexto y los errores. Cada owner guarda su campo context, y cuando un componente llama a useContext, Solid no consulta el árbol del DOM, sino que trepa de padre en padre por el árbol de owners hasta encontrar el proveedor más cercano que fijó ese valor. La herencia de contexto es la estructura de pertenencia recorrida hacia arriba.

TSX
import { createContext, useContext } from "solid-js";const Tema = createContext(() => "claro");function Boton() {  const tema = useContext(Tema); // sube por owners hasta el proveedor mas cercano  return <button class={tema()}>ok</button>;}

Los límites de error operan sobre el mismo árbol en sentido inverso: instalan un manejador en su owner, y cuando una computación descendiente lanza una excepción, el error asciende de hijo a padre hasta el primer manejador que lo capture. Disposición, contexto y errores comparten una sola columna vertebral, y esa es la razón de que capturar el owner —lo que harás con getOwner en la próxima lección— te dé acceso a las tres cosas de golpe: no capturas “un ámbito de limpieza”, capturas el nodo entero con su posición en el árbol.