onCleanup: liberar al desmontar
onCleanup en componentes: la mitad simétrica de onMount que libera intervalos, listeners y suscripciones cuando el dueño se destruye. Cómo se ata al owner, por qué es más flexible que un onUnmount, y el orden exacto de la limpieza: los hijos antes que los padres en el árbol, y dentro de un mismo dueño en orden inverso al registro (LIFO).
Liberar lo que reservaste
El patrón canónico empareja adquisición y liberación en el mismo bloque. Lo que abres en onMount, lo cierras en un onCleanup inmediato, de modo que ambas mitades se leen juntas y ninguna se olvida.
import { onMount, onCleanup } from "solid-js";function Reloj() { let etiqueta!: HTMLTimeElement; onMount(() => { const id = setInterval(() => { etiqueta.textContent = new Date().toLocaleTimeString(); }, 1000); onCleanup(() => clearInterval(id)); // corre al desmontar }); return <time ref={etiqueta} />;}Cuando el componente se desmonta, Solid ejecuta esa función y el intervalo se detiene. No hay que recordar hacerlo desde fuera ni orquestar ningún desmontaje manual: la liberación viaja pegada a la reserva.
onCleanup no está limitado a onMount. Puede invocarse en cualquier punto de un dueño reactivo —en el cuerpo del componente, dentro de un createEffect, dentro de otro onCleanup— y siempre queda registrado en el dueño vigente. En el cuerpo del componente, ese dueño es el componente entero, así que la limpieza corre al desmontar.
function Documento() { const alPresionar = (e: KeyboardEvent) => { if (e.key === "Escape") cerrar(); }; document.addEventListener("keydown", alPresionar); onCleanup(() => document.removeEventListener("keydown", alPresionar)); // en el cuerpo, se limpia al desmontar return <dialog>...</dialog>;}La simetría con onMount
onMount y onCleanup forman una pareja: uno establece una relación con el mundo, el otro la deshace. No son dos hooks independientes que casualmente se complementan, sino los dos extremos de un mismo hecho. Mientras el componente vive, existe el intervalo; cuando muere, no debe existir. Esa correspondencia es lo que la pareja mantiene.
La diferencia con un onUnmount al estilo clásico es que onCleanup no depende de estar en un único hook de cierre. Como se ata al dueño, puedes registrar la limpieza exactamente donde reservaste el recurso, por muchos que sean y por dispersos que estén. Cada adquisición lleva su liberación al lado, en lugar de acumularse todas en una función de desmontaje lejana que hay que mantener sincronizada a mano.
El orden de la limpieza
Cuando un dueño se destruye, Solid no ejecuta las limpiezas en un orden arbitrario: sigue una disciplina precisa y determinista, en dos ejes.
En el árbol de dueños, la limpieza es de abajo hacia arriba: los hijos se disponen antes que su padre. Solid recorre primero el subárbol de dueños hijos —recursivamente— y solo después ejecuta las limpiezas propias del dueño. Un componente anidado se desmonta por completo antes de que corra el onCleanup del componente que lo contiene.
Dentro de un mismo dueño, las limpiezas corren en orden inverso al de su registro: la última que registraste es la primera en ejecutarse. Es la disciplina de una pila, y tiene sentido físico: lo último que se abrió suele ser lo primero que hay que cerrar, porque puede depender de lo que se abrió antes.
En la práctica rara vez tienes que pensar en el orden exacto si cada recurso es independiente. Importa cuando hay dependencias entre recursos: si el recurso B se construyó a partir del A, registra B después que A y el orden inverso garantiza que B se cierre antes de que A desaparezca bajo sus pies.
function Sesion() { onMount(() => { const conexion = abrirConexion(); onCleanup(() => conexion.cerrar()); // se registra primero -> se limpia despues const canal = conexion.abrirCanal(); // depende de la conexion onCleanup(() => canal.desuscribir()); // se registra despues -> se limpia primero }); return <div>...</div>;}Qué se libera y desde dónde
Casi todo lo que hay que liberar cae en cuatro familias, y todas siguen el mismo par de abrir y registrar. Un caso muy frecuente más allá del intervalo es suscribirse a una API del navegador o a un store externo:
function Tema() { const consulta = window.matchMedia("(prefers-color-scheme: dark)"); const alCambiar = (e: MediaQueryListEvent) => aplicarTema(e.matches); consulta.addEventListener("change", alCambiar); onCleanup(() => consulta.removeEventListener("change", alCambiar)); return <div>...</div>;}El recurso vive fuera de Solid —lo posee el navegador—, pero su suscripción queda atada al dueño del componente y se retira sola al desmontar. Ese es el valor de onCleanup: convierte cualquier recurso externo en algo con el ciclo de vida del componente.