wandres.dev
OWNERSHIP Y CLEANUP · onCleanup, getOwner

onMount y la disposición automática

onMount es un createEffect que no rastrea nada: corre una sola vez, tras el primer render y solo en el cliente. Junto a la disposición automática de owners, cierra el ciclo de vida del componente sin necesidad de un onUnmount.

⏱ 12 min

Solid no tiene un onUnmount, y no es un olvido: es una consecuencia elegante del árbol de ownership. El montaje y el desmontaje no son dos hooks simétricos que debas emparejar, sino dos caras del mismo owner. onMount cubre la entrada —código que corre una vez cuando el DOM ya existe— y onCleanup cubre la salida, disparado automáticamente cuando el owner se dispone. Entender qué es realmente onMount por dentro, y cómo Solid desmonta componentes sin que tú lo pidas, completa el modelo del ciclo de vida.

🎯 Al terminar esta lección sabrás
  • Entender que onMount es un createEffect que no rastrea nada y corre una sola vez.
  • Saber cuándo corre —tras el render, solo en el cliente— y por qué no en el servidor.
  • Situar onMount frente a createRenderEffect, createEffect y el cuerpo del componente.
  • Comprender la disposición automática: por qué no existe ni hace falta un onUnmount.

onMount es un efecto que no rastrea nada

Conceptualmente, onMount no es una primitiva nueva: es azúcar sobre un efecto que no lee ningún signal.

// idea de la implementacion de onMount
import { createEffect, untrack } from "solid-js";

function onMount(fn) {
  createEffect(() => untrack(fn));   // sin lecturas rastreadas: corre una vez
}

La lógica es limpia. Un createEffect corre después del render y se re-ejecuta cuando cambia alguna de sus dependencias. Si envuelves su cuerpo en untrack, no se suscribe a nada; y si no se suscribe a nada, no hay ningún cambio que pueda dispararlo de nuevo. Resultado: corre exactamente una vez, en la fase de efectos, con el DOM ya construido. Por eso onMount es el lugar canónico para lo que necesita el DOM presente: medir un nodo, dar foco a un campo, inicializar una librería que espera un elemento real.

import { onMount, onCleanup } from "solid-js";

function Mapa() {
  let el;                             // ref al contenedor
  onMount(() => {
    const mapa = crearMapa(el);       // el nodo ya existe en el DOM
    onCleanup(() => mapa.destruir()); // salida emparejada con la entrada
  });
  return <div ref={el} />;
}

Cuándo corre, y por qué no en el servidor

onMount corre después del render inicial y de los createRenderEffect que el compilador usa para poblar el DOM, de modo que cuando entra, las refs ya apuntan a nodos reales. Y corre solo en el cliente: durante el renderizado en servidor, Solid no ejecuta la fase de efectos —el HTML se genera de forma síncrona sin ellos—, así que ningún onMount se dispara en el servidor. Esa es la razón de fondo por la que onMount es el refugio seguro para código que toca window, document o cualquier API del navegador.

🏗️

Cuerpo

Corre al crear, en cliente y servidor. El DOM aún no existe. Cablea el grafo.

🎨

createRenderEffect

Corre durante el render, antes de pintar. Lo usa el compilador para el DOM.

🚀

onMount

Corre una vez tras el render, solo en cliente. El DOM ya está montado.

🔁

createEffect

Corre tras el render y se repite con sus dependencias. Efectos que reaccionan.

Como onMount no rastrea, no reacciona a cambios de props. Si necesitas reaccionar cuando una prop cambie, ese es el trabajo de createEffect, no de onMount. Confundirlos —esperar que onMount se repita— es un error clásico de quien llega buscando el equivalente de un useEffect con array de dependencias vacío.

📝
onMount no recibe función de limpieza

A diferencia del useEffect de React, onMount no devuelve ni recibe un teardown. La limpieza se registra con onCleanup, y puedes ponerla dentro del propio onMount, justo al lado de lo que abres. Montaje y desmontaje se leen juntos, pero se disparan en momentos distintos: la entrada una vez tras el render, la salida al disponer el owner.

Medir y tocar el DOM ya construido

El uso estrella de onMount es leer o manipular el DOM real: medir tamaños, posicionar algo respecto a un nodo, arrancar una animación de entrada o conectar una librería imperativa. Todo eso exige que el elemento exista, y onMount es el primer punto del ciclo donde eso está garantizado en el cliente.

import { onMount, createSignal } from "solid-js";

function Medible() {
  let caja;
  const [alto, setAlto] = createSignal(0);

  onMount(() => {
    setAlto(caja.getBoundingClientRect().height); // el nodo ya esta pintado
  });

  return <div ref={caja}>alto medido: {alto()}px</div>;
}

Si intentaras esa medición en el cuerpo del componente, caja aún sería undefined: el cuerpo corre durante la creación, antes de que el JSX se convierta en nodos y se inserte en el documento. El cuerpo cablea; onMount actúa sobre lo ya cableado y pintado.

ℹ️
onMount e hidratación

En una app con render en servidor, el HTML llega ya pintado y el cliente lo hidrata: reengancha la reactividad sobre el marcado existente. Los onMount corren durante esa hidratación, en el cliente, una vez por componente. Por eso siguen siendo el lugar correcto para lógica que solo tiene sentido en el navegador, incluso cuando el primer HTML lo generó el servidor: el servidor produjo el marcado, pero tu onMount solo despierta del lado del cliente.

La disposición automática de componentes

Cada vez que se monta un componente, su cuerpo corre dentro de un owner. Ese owner cuelga del árbol que vimos en la primera lección, y su destino está atado al del subárbol del DOM que produce. Cuando el componente sale de la interfaz —porque un Show conmutó, un For perdió un elemento, un Switch cambió de caso o un Dynamic reemplazó el componente—, Solid dispone ese owner: recorre su subárbol y ejecuta todas las limpiezas de abajo arriba.

flowchart TD
A[montar componente] --> B[correr cuerpo una vez]
B --> C[render effects pueblan el DOM]
C --> D[onMount corre una vez]
D --> E[updates de grano fino]
E --> F[el control de flujo lo quita]
F --> G[disponer el owner]
G --> H[correr todos los onCleanup]
style D fill:#a6e3a1,color:#11111b
style G fill:#f9e2af,color:#11111b
style H fill:#f38ba8,color:#11111b

Esto es lo que elimina la necesidad de un onUnmount. No hay un evento de desmontaje al que suscribirse porque el desmontaje no es un evento del componente: es la disposición de su owner, y ya tienes onCleanup para engancharte a ella. Los componentes de control de flujo crean owners anidados para cada rama y los disponen al cambiar; la raíz de todo, creada por render, dispone el árbol entero cuando llamas a la función que render devuelve.

Un ejemplo hace tangible la cascada. Cada rama de un Show es un subárbol de owners que nace y muere con la condición:

import { Show, onCleanup } from "solid-js";

function Pestana(props) {
  return (
    <Show when={props.abierta()}>
      {(() => {
        const canal = suscribir(props.id);
        onCleanup(() => canal.cerrar()); // corre al cerrar la pestana
        return <Panel canal={canal} />;
      })()}
    </Show>
  );
}

Cuando abierta pasa a falso, Solid dispone por completo el owner de esa rama: el onCleanup cierra el canal y cualquier efecto interno se desmonta, sin que exista ningún onUnmount de por medio. Volver a abrirla no reanuda nada: crea un owner nuevo desde cero, con su propio ciclo de vida.

Un solo mecanismo para toda la salida

La ausencia de onUnmount en Solid es una lección de diseño por sustracción. Otros frameworks multiplican los ganchos de ciclo de vida —montar, actualizar, desmontar— y te obligan a repartir la lógica de un mismo recurso entre dos o tres de ellos, con el riesgo perpetuo de que el emparejamiento se rompa al editar. Solid colapsa toda la salida en un único mecanismo: la disposición del owner, sobre la que onCleanup te da un asa uniforme. Da igual si el recurso lo abriste en el cuerpo, en un onMount, en un efecto que se repite o en un ámbito asíncrono re-entrado con runWithOwner: su limpieza vive en el mismo árbol y corre por la misma razón. Esa uniformidad no es solo comodidad, es corrección: cuando solo hay un camino de salida, no hay caminos que olvidar. Y onMount, lejos de ser un hook especial, se revela como lo que es —un efecto que no rastrea—, lo que confirma que en Solid casi todo el ciclo de vida se reduce a dos ideas ya conocidas: computaciones que corren y owners que se disponen. Menos primitivas, cubriendo más casos, con menos formas de equivocarse.

⚔️ Comprueba el ciclo completo
  1. Pon un console.log en el cuerpo, uno en un createRenderEffect, uno en onMount y uno en un createEffect; ordena en consola quién corre antes y explica por qué.
  2. Inicializa una librería en onMount usando una ref y libérala en un onCleanup contiguo; monta y desmonta el componente con un Show y verifica ambos disparos.
  3. Cambia una prop y confirma que onMount no se repite; mueve la lógica a un createEffect y observa que ahora sí reacciona.
  4. Renderiza el componente en servidor y comprueba que su onMount no corre allí.
  5. Explica en una sola frase por qué Solid no necesita un onUnmount.