wandres.dev
CREATEEFFECT · reacciones a los signals

Variantes de timing: render effect y computed

La misma reacción con relojes distintos. createRenderEffect corre durante el render, antes de montar el DOM; createComputed corre en la fase pura, inmediato. El orden computed, render effect, effect y cuándo usar cada uno.

⏱ 14 min

createEffect no es la única forma de reaccionar. Es la más común y la que deberías usar casi siempre, pero Solid ofrece dos hermanas que hacen lo mismo —trackear dependencias y re-ejecutarse— en momentos distintos del ciclo. createComputed corre lo antes posible, dentro de la fase pura; createRenderEffect corre durante el render, antes de que el DOM se monte; createEffect corre al final, cuando el DOM ya existe. Elegir bien la variante es elegir el instante exacto en que tu reacción toca el mundo.

🎯 Al terminar esta lección sabrás
  • Distinguir las tres computaciones: createComputed, createRenderEffect y createEffect.
  • Situar cada una en el ciclo: la pura, la de render y la de después del render.
  • Saber cuándo usar createRenderEffect para actuar antes del pintado.
  • Entender por qué createComputed casi siempre se sustituye por createMemo.

Tres relojes para una misma reacción

Las tres funciones comparten anatomía: reciben un cuerpo, lo corren en un scope reactivo, trackean lo que leen y vuelven a correr cuando cambia. Lo único que las separa es su lugar en el reloj del ciclo de actualización.

createComputed es el más impaciente. Su primer run ocurre de inmediato, síncronamente, en el acto de crearlo, y sus reruns suceden en la fase pura, antes que cualquier render effect o effect. Nació para un caso: escribir en otros signals durante la propagación.

createRenderEffect corre durante la fase de render. Su primer run también es inmediato —no se difiere—, pero su propósito es actuar sobre el DOM justo antes de que se monte y se pinte. De hecho, así es como el propio Solid implementa por dentro sus bindings al DOM.

createEffect es el más paciente. Su primer run se difiere hasta después del render, y sus reruns corren cuando el DOM ya está construido y actualizado. Es el lugar natural para efectos secundarios que necesitan ver el DOM ya montado o hablar con el exterior.

import { createComputed, createRenderEffect, createEffect } from "solid-js";

createComputed(() => console.log("1 puro"));
createRenderEffect(() => console.log("2 render"));
createEffect(() => console.log("3 efecto"));
// consola: 1 puro, 2 render, 3 efecto
flowchart LR
C[createComputed puro e inmediato] --> RE[createRenderEffect durante el render]
RE --> EF[createEffect tras montar el DOM]
style C fill:#cba6f7,color:#11111b
style RE fill:#89b4fa,color:#11111b
style EF fill:#a6e3a1,color:#11111b

El orden dentro de un tick

Cuando un lote se propaga, Solid respeta siempre la misma secuencia: primero las computaciones puras —memos y computeds—, después los render effects, y al final los effects. Ese orden no es arbitrario: refleja una dependencia de madurez.

Los computeds pueden alimentar a otros nodos, así que van primero para que todo lo derivado esté listo. Los render effects preparan el DOM. Los effects, ya con el DOM montado y el grafo cerrado, son los últimos en tocar el mundo.

⚙️

createComputed

Fase pura, inmediato. Corre antes que nadie y puede escribir signals dentro del ciclo. Potente y peligroso: escribir a mitad de la actualización obliga a recomputar de más. En código de aplicación casi nunca es la respuesta correcta.

🎨

createRenderEffect

Fase de render, antes de montar el DOM. Para leer o mutar el DOM antes del primer pintado y evitar un parpadeo: fijar scroll, medir, sincronizar un widget de terceros sin que se vea el salto.

createEffect

Después del render, primer run diferido. El destino del 99% de tus efectos: logging, red, suscripciones, DOM ya montado. Si dudas cuál usar, es este.

Cuándo usar cada una

La regla práctica es corta. Por defecto, createEffect: es el que espera a que el DOM exista y no te sorprende con timing raro. Recurre a createRenderEffect solo cuando de verdad necesitas intervenir antes de que el DOM producido por este render se muestre, para no pintar un estado intermedio que el usuario alcance a ver.

// fijar el scroll antes del pintado evita el salto visible
createRenderEffect(() => {
  if (ref) ref.scrollTop = posicionGuardada();
});

createComputed merece un aviso aparte. Si lo que quieres es derivar un valor, no lo uses: usa createMemo, que además cachea y es leíble.

// NO: computed para derivar
createComputed(() => setDoble(n() * 2));

// SI: un memo, cacheado y sin signal espejo
const doble = createMemo(() => n() * 2);

Un memo es, en esencia, un computed con salida memorizada y consumible. La única razón legítima para createComputed es escribir a un signal de forma síncrona como parte del grafo, y hasta ese caso conviene mirarlo dos veces, porque escribir a mitad de la propagación puede forzar pasadas de recomputación adicionales.

ℹ️
onMount es un createEffect que corre una vez

onMount(fn) no es una primitiva aparte: equivale a un createEffect cuyo cuerpo no trackea nada, de modo que corre una única vez tras el render y jamás reacciona. Cuando solo quieres ejecutar algo al montar —enfocar un input, medir el layout inicial, arrancar una librería— onMount expresa esa intención con más claridad que un efecto sin dependencias. Por dentro, sin embargo, es el mismo mecanismo con el listener apagado.

📝
En el servidor los efectos no corren

Bajo SSR con SolidStart, createEffect y createRenderEffect no se ejecutan durante el render del servidor: solo el HTML se emite, y los efectos arrancan al hidratar en el cliente. Por eso el código que toca window, document o el DOM vive con naturalidad dentro de un efecto: allí tienes la garantía de estar en el navegador. Ponerlo en el cuerpo del componente, que sí corre en el servidor, es la receta del clásico error window is not defined.

⚠️
createComputed no es el createEffect síncrono

Un error frecuente al llegar de otros frameworks es alcanzar createComputed porque suena a “efecto pero antes”. No es eso: es una computación pura pensada para el grafo, no para efectos secundarios sobre el mundo. Poner ahí un fetch, un log o una manipulación del DOM lo ejecuta en un momento en que el DOM aún no existe y el grafo aún se está resolviendo. Para efectos, createEffect. Para derivar, createMemo. createComputed es una herramienta de biblioteca, rara en código de aplicación.

No son tres APIs: es una computación con tres horarios

La lección profunda es que Solid no te ha dado tres primitivas distintas, sino una sola idea de computación reactiva expuesta con tres políticas de planificación. Por dentro, createComputed, createRenderEffect, createEffect y hasta createMemo son el mismo tipo de nodo: una función que se ejecuta en un scope de tracking, recolecta dependencias y se reprograma cuando estas cambian. Lo único que varía es en qué cola entra y cuándo se vacía esa cola. createMemo añade una casilla que guarda el último valor y lo hace leíble; los demás no la tienen y por eso son sumideros. Esta unificación es el secreto de la simplicidad de Solid: su núcleo reactivo cabe en una sola abstracción, y toda la superficie de la API —memos, computeds, render effects, effects— son parametrizaciones de esa abstracción sobre dos ejes, si el resultado es leíble y en qué fase corre. Cuando lo ves así, dejas de memorizar cuatro funciones con reglas separadas y empiezas a razonar sobre un único mecanismo cuyo horario ajustas según necesites que la reacción ocurra antes de derivar, antes de pintar o después de montar. Y comprendes, de paso, por qué elegir mal la variante no es un detalle de estilo: es pedir que tu reacción ocurra en un instante en que el mundo aún no está —o ya no está— en el estado que ella supone.

⚔️ Mide el reloj de cada variante
  1. Coloca un createComputed, un createRenderEffect y un createEffect con logs y confirma en consola el orden computed, render effect, effect.
  2. Usa createRenderEffect para fijar el scrollTop de un contenedor y compáralo con hacerlo en createEffect: observa el parpadeo en el segundo caso.
  3. Toma un efecto que solo hacía setX(algo) para derivar y reescríbelo como createMemo; razona qué pasada de más te ahorras.
  4. Explica con tus palabras por qué createMemo puede describirse como un createComputed con salida memorizada.