Condicionales y early return
Por qué un if en el cuerpo del componente rompe la reactividad, y por qué en Solid se ramifica con Show y Switch dentro del JSX, no con control de flujo imperativo ejecutado una sola vez.
El reflejo más seductor de React —poner if (cargando) return <Spinner /> al principio del componente— es un bug en Solid. Como el cuerpo corre una sola vez, un if en el cuerpo evalúa su condición una vez y congela la rama para siempre. La ramificación que debe reaccionar tiene que vivir dentro del JSX, donde el compilador puede reevaluarla. Para eso existen Show, Switch y Dynamic: son el flujo de control de un lenguaje reactivo.
- Entender por qué un
ifen el cuerpo se evalúa una sola vez. - Ver por qué un early return congela la rama elegida al montar.
- Ramificar de forma reactiva con
Show,SwitchconMatchyDynamic. - Conocer la prop
fallbacky el modificadorkeyeddeShow.
Un if en el cuerpo se evalúa una vez
function Roto(props) {
// el cuerpo corre UNA vez: este if usa el valor inicial y ya
if (props.cargando) {
return <Spinner />; // si arranca cargando, se queda en Spinner PARA SIEMPRE
}
return <Datos valor={props.datos} />;
}
props.cargando se lee una sola vez, cuando corre el cuerpo. Si era true al crearse, el componente devuelve <Spinner /> y no vuelve a evaluarse jamás; cuando cargando pase a false, nada re-ejecuta el cuerpo, así que el spinner se queda clavado. El return cortocircuitó el grafo: eligió una rama en el instante del montaje y la fijó. El if no es reactivo porque el cuerpo no es reactivo.
Ramificar dentro del JSX: Show
Show reevalúa su prop when de forma reactiva porque vive en el JSX, que el compilador rastrea:
import { Show } from "solid-js";
function Bien(props) {
return (
<Show when={!props.cargando} fallback={<Spinner />}>
<Datos valor={props.datos} />
</Show>
);
}
when es una expresión que el compilador envuelve en un efecto; cuando props.cargando cambia, Show intercambia entre sus hijos y el fallback. Por defecto, Show no recrea los hijos mientras la verdad de when no cambie de valor booleano. Con keyed fuerza la recreación cuando cambia la identidad del valor y te lo pasa, ya estrechado, como argumento de una función:
<Show when={usuario()} keyed fallback={<p>anonimo</p>}>
{(u) => <Perfil id={u.id} />}
</Show>
Varias ramas: Switch, Match y Dynamic
Para más de dos ramas, Switch con Match en lugar de cadenas de else if. Solo se monta el primer Match cuyo when sea verdadero, y todo reacciona:
import { Switch, Match } from "solid-js";
<Switch fallback={<p>desconocido</p>}>
<Match when={estado() === "cargando"}><Spinner /></Match>
<Match when={estado() === "error"}><ErrorVista /></Match>
<Match when={estado() === "listo"}><Datos /></Match>
</Switch>
Cuando lo que cambia es qué componente montar, Dynamic despacha por valor sin escribir tú el condicional:
import { Dynamic } from "solid-js/web";
const registro = { alfa: Alfa, beta: Beta };
<Dynamic component={registro[tipo()]} etiqueta="hola" />
flowchart TD A[la condicion cambia] --> B[if en el cuerpo] A --> C[Show en el JSX] B --> D[no se reevalua nunca] C --> E[el efecto reevalua el when] E --> F[intercambia hijos o fallback] style D fill:#f38ba8,color:#11111b style F fill:#a6e3a1,color:#11111b
Conviene notar que los hijos de Show pueden ser una función, no solo elementos. La forma de función se evalúa de manera perezosa y evita construir el subárbol hasta que la condición se cumple, lo que ahorra trabajo cuando el contenido es caro.
// el contenido caro no se crea mientras when sea falso
<Show when={abierto()}>
{() => <PanelPesado datos={datos()} />}
</Show>
Y cuando ninguna rama aplica, Switch y Show aceptan fallback; Dynamic puede combinarse con Show para elegir componente y, a la vez, cubrir el hueco mientras se decide.
// elegir componente y cubrir el hueco a la vez
<Show when={cargado()} fallback={<Spinner />}>
<Dynamic component={vistas[tipo()]} />
</Show>
En Solid, las ramas no elegidas no cuestan: Show no crea el subárbol de sus hijos hasta que when es verdadero, y lo desecha cuando deja de serlo. No hay que envolver nada en comprobaciones manuales para evitar trabajo, porque no hay render y la rama inactiva sencillamente no existe en el DOM.
Iterar también es flujo de control
La misma lógica se extiende a las listas: recorrer una colección es otra forma de control de flujo que debe vivir en el JSX. Un .map() en el cuerpo se evaluaría una vez y quedaría congelado; por eso Solid ofrece For e Index.
import { For, Index } from "solid-js";
// For: se identifica por REFERENCIA del elemento. Reordenar mueve nodos.
<For each={items()}>
{(item, i) => <li>{i()}: {item.texto}</li>}
</For>
// Index: se identifica por POSICION. El elemento es un signal.
<Index each={items()}>
{(item, i) => <li>{i}: {item().texto}</li>}
</Index>
La diferencia es profunda y es puramente reactiva: For reconcilia por identidad del elemento, así que si reordenas la lista, mueve los nodos del DOM ya creados; cada item es un valor fijo y el índice i es un signal. Index reconcilia por posición: cada item es un signal y el índice es fijo. Usa For para listas de objetos que se reordenan; Index para primitivos o para campos de formulario donde la posición manda.
Los operadores nativos del JSX —el ternario y el &&— también son reactivos, porque el compilador los rastrea. Pero Show gana cuando necesitas un fallback o el modificador keyed, y Switch gana cuando hay tres o más ramas, porque evita anidar ternarios ilegibles.
La pregunta que resuelve qué usar no es sintáctica, es reactiva: ¿qué es lo que cambia? Si cambia si algo se muestra, Show. Si cambia cuál de varias ramas, Switch. Si cambia el conjunto de elementos, For. Si cambia el contenido de posiciones fijas, Index. Cada uno reconcilia la parte mínima del DOM que su unidad de cambio implica, y ninguno vuelve a ejecutar el cuerpo del componente.
En un modelo imperativo el flujo de control es un evento único: el if decide, el return sale, y ahí acaba todo. Solid te obliga a ascender el flujo de control al mismo rango que cualquier otro valor reactivo, porque en un grafo “qué rama está activa” es solo otra cosa que puede cambiar con el tiempo, y todo lo que cambia con el tiempo debe vivir donde el grafo lo vea: dentro del JSX. Show, Switch y Dynamic no son azúcar ergonómico sobre if; son el if, el switch y el despacho dinámico de un lenguaje reactivo, expresados como nodos del grafo y no como sentencias ejecutadas una vez. Por eso pueden hacer lo que un if desnudo nunca podría: intercambiar subárboles sin tocar a los hermanos, conservar o descartar estado a través de un toggle con keyed, mostrar un fallback durante el hueco. En el momento en que dejas de alcanzar early returns y empiezas a pensar “la rama es una expresión reactiva”, cae la última pieza del pensamiento imperativo de React, y el JSX se convierte en lo que de verdad es en Solid: la descripción declarativa de un grafo que se reconfigura solo cuando cambian sus entradas.
- Escribe el antipatrón: un early return con
if (props.cargando)y demuestra que al cambiarcargandoafalseel Spinner no desaparece. - Reescríbelo con
Showyfallback, y confirma que ahora sí reacciona al cambio. - Convierte una cadena de
else ifcon tres estados en unSwitchconMatchy unfallback. - Usa
Showconkeyedpara recrear el hijo cuando cambie la identidad del valor, y explica en qué se diferencia delShownormal. - Reescribe un
.map()que tuvieras en el cuerpo como un<For>y comprueba que al reordenar la lista se mueven los nodos en vez de recrearse.