El componente corre una vez
El cuerpo de un componente Solid no es una función de render: es una factoría que se ejecuta una sola vez, al crear el componente. Qué significa eso y qué consecuencias arrastra en todo el modelo.
El cuerpo es un constructor, no un render
La forma más rápida de sentirlo es un console.log suelto en el cuerpo:
import { createSignal } from "solid-js";function Contador() { console.log("cuerpo ejecutado"); // se imprime UNA sola vez const [n, setN] = createSignal(0); return <button onClick={() => setN(n() + 1)}>{n()}</button>;}Pulsa el botón cien veces: cuerpo ejecutado aparece exactamente una vez. En React se imprimiría cien veces, porque la función se re-ejecuta en cada render. Aquí no. El console.log, la llamada a createSignal, la declaración de la const: todo ocurre en un único instante, el del montaje. Lo único que se actualiza con cada clic es el nodo de texto {n()}, porque el compilador de Solid lo transformó en un efecto de grano fino que se re-ejecuta por su cuenta.
Qué implica “una vez”
El ciclo de vida de un componente Solid no es un bucle, es una recta con un montaje y una propagación posterior:
De ese “una vez” se desprende casi todo lo demás:
- Una
constse calcula una sola vez. Si escribesconst doble = n() * 2obtienes un número congelado, no algo reactivo. Lo verás a fondo en el nivel de closures. - No existen los stale closures por re-render, porque no hay re-render; pero sí hay que leer los signals como funciones para que sigan vivos.
- El coste de montar se paga una vez. Las actualizaciones jamás vuelven a tocar tu lógica de creación: no hay nada que memoizar para “evitar recalcular en el próximo render”.
- Un
console.logen el cuerpo depura el montaje, no el ciclo reactivo. Para observar la reactividad hay que mirar dentro de los efectos.
onMount, onCleanup y el owner
El cuerpo se ejecuta dentro de un owner: un ámbito reactivo que Solid usa para rastrear pertenencia y limpieza. onMount registra una llamada que corre una vez tras el primer render, cuando el DOM ya existe. onCleanup registra la limpieza y puede invocarse en cualquier punto de un owner, incluso dentro de un efecto.
import { createSignal, onMount, onCleanup } from "solid-js";function Reloj() { const [hora, setHora] = createSignal(new Date()); onMount(() => { const id = setInterval(() => setHora(new Date()), 1000); onCleanup(() => clearInterval(id)); // corre al desmontar }); return <time>{hora().toLocaleTimeString()}</time>;}No hay array de dependencias ni regla de exhaustive-deps que vigilar: la creación ocurre una vez y la limpieza es explícita. El intervalo queda atado al ciclo de vida del owner, y cuando el componente se desmonta, onCleanup lo detiene sin que tú tengas que acordarte en cada render.
No hay reglas de hooks
Como el cuerpo corre una vez y createSignal es solo una función que devuelve un par getter/setter, Solid no arrastra las “reglas de los hooks” de React. Puedes crear primitivos dentro de un if, de un bucle o después de una condición, porque no dependen de un orden de llamada estable entre renders: no hay renders que alinear.
function Panel(props) { const columnas = []; // legal en Solid: crear signals en un bucle, prohibido en React for (const clave of props.claves) { columnas.push([clave, createSignal(0)]); } return ( <For each={columnas}> {([clave, [valor]]) => <span>{clave}: {valor()}</span>} </For> );}Lo que sí permanece es la disciplina del owner: los primitivos creados durante el cuerpo pertenecen al owner del componente y se destruyen con él. Si creas un efecto fuera de un owner —por ejemplo, dentro de un setTimeout— queda huérfano y no se limpia solo; para eso existe createRoot, que verás más adelante. La ausencia de reglas de orden es consecuencia directa de que el cuerpo no se repite: el orden solo importaría si hubiera un segundo pase, y no lo hay.
La distancia con React se ve mejor en la consola. Allí el cuerpo es la función de render y se re-ejecuta en cada actualización; aquí, jamás:
// React: el cuerpo corre en CADA renderfunction ContadorReact() { console.log("render"); // se imprime en cada actualizacion const [n, setN] = useState(0); return <button onClick={() => setN(n + 1)}>{n}</button>;}Un vistazo de conjunto a qué se ejecuta y cuándo: