Saltar al contenido

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:

TSX
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 const se calcula una sola vez. Si escribes const doble = n() * 2 obtienes 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.log en 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.

TSX
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.

TSX
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:

TSX
// 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: