Saltar al contenido

El cambio de mentalidad frente a React

No hay renders. Dejar de pensar en cuándo se re-renderiza y empezar a pensar en el grafo: qué depende de qué. La síntesis del modelo de ejecución de Solid frente a los reflejos de React.

Borra la palabra “render”

La diferencia no es de rendimiento, es de ontología. Son dos máquinas distintas:

  • React: el componente ES una función de render que corre en cada actualización. Optimizas para que corra menos: memo, useMemo, useCallback, dividir componentes.
  • Solid: el componente es una factoría que corre una vez. No hay nada que optimizar en ese sentido: no vuelve a correr nunca.
  • React: useEffect(fn, [deps]); tú declaras las dependencias y un linter te vigila.
  • Solid: createEffect(fn); las dependencias se rastrean solas al leer signals dentro. No hay array.
  • React: un setState provoca el re-render de un subárbol y una reconciliación.
  • Solid: un signal actualiza solo los efectos y nodos del DOM que lo leen. No hay subárbol, no hay reconciliación.

El mismo useEffect que en React vigilas con un array de dependencias, en Solid se escribe sin él y no puede desincronizarse:

TSX
// React: si olvidas userId en el array, el efecto usa un valor ranciouseEffect(() => { cargar(userId); }, [userId]);// Solid: lees userId dentro; la suscripcion es exacta por construccioncreateEffect(() => { cargar(userId()); });

Ese array es la fuente de una familia entera de bugs de React —dependencias omitidas, valores rancios, efectos que no se re-disparan— que en Solid no tiene dónde existir, porque nunca declaras dependencias: las lees.

Traduce los reflejos, no la sintaxis

Copiar la sintaxis del hook sin cambiar de modelo es la fuente de la mitad de la frustración. La traducción correcta empieza en la intención:

Preguntas nuevas para un modelo nuevo

El cambio se nota en las preguntas que dejas de hacerte. Cada ansiedad de React tiene un reemplazo o simplemente desaparece:

  • En vez de “¿por qué se re-renderiza de más?”, pregunta “¿qué efecto se re-ejecuta y qué signal lo dispara?”.
  • En vez de “¿debo envolver esto en useMemo?”, pregunta “¿este derivado es caro o compartido? Entonces createMemo; si no, una función basta”.
  • En vez de “¿están bien mis dependencias?”, nada: no hay array. Lee el signal donde lo necesites y ya queda suscrito.
  • En vez de “¿este objeto rompe la igualdad referencial?”, nada: no hay comparación de props ni de árboles. El problema no existe.
TSX
// React: memoizar para no recalcular en cada renderconst total = useMemo(() => items.reduce(suma, 0), [items]);// Solid: los items son un signal; el derivado se recalcula solo si cambianconst total = createMemo(() => items().reduce(suma, 0));// y si es barato, ni memo: const total = () => items().reduce(suma, 0);

El patrón se repite en toda la API. Cada pieza de React que existía para controlar el render —memo, useMemo, useCallback, useRef para estabilizar identidades— no tiene contraparte en Solid, porque no hay render que controlar. La API no es más pequeña por elegancia: lo es porque la mitad de la de React resolvía un problema que aquí no existe.

Lo que ganas al soltar el render

El cambio no es solo conceptual; tiene consecuencias muy concretas en el código que escribes y en el que ya no escribes:

  • Desaparece la memoización defensiva. No hay useCallback ni memo de componente porque no hay identidad de función que estabilizar entre renders. Un handler declarado en el cuerpo se crea una vez y es estable por construcción.
  • Desaparecen los arrays de dependencias. El rastreo es automático y exacto: nunca “olvidas” una dependencia ni suscribes de más. El linter deja de pedirte listas.
  • El estado profundo es barato. Con createStore, mutar estado.a.b.c solo notifica a quien leyó ese camino; no clonas objetos ni comparas árboles para detectar el cambio.
  • La granularidad es del tamaño de un nodo. Actualizar un texto toca un textContent, no re-ejecuta un componente ni difunde un re-render hacia abajo.
TSX
// React: el handler cambia de identidad salvo que lo memoicesconst alClic = useCallback(() => setN((n) => n + 1), []);// Solid: se declara una vez, es estable, no hay nada que memoizarconst alClic = () => setN(n() + 1);

Ninguna de estas ventajas es un “modo optimizado” que actives: son la consecuencia de no tener un render que repetir. El trabajo que en React dedicabas a controlar el bucle, en Solid simplemente no existe, y ese hueco es el que ocupa la claridad del grafo.

Y cuando de verdad buscas rendimiento extremo, el camino no es memoizar más, sino afinar el grafo: compartir un cálculo con createMemo, o agrupar varias escrituras con batch para propagarlas en una sola pasada.

TypeScript
import { batch } from "solid-js";// una sola propagacion aunque muevas tres signalsbatch(() => {  setNombre("Ada");  setEdad(36);  setActivo(true);});

Ninguno de estos ajustes se parece a “evitar renders”: todos son “dar forma al grafo”. Ese es, exactamente, el vocabulario nuevo que sustituye al viejo.