Por que no usar && ni ternarios crudos
Los operadores nativos del JSX reaccionan en Solid, pero and-and filtra valores falsy al DOM y ni el ternario ni el and te dan fallback, keyed, accesor estrechado ni exclusividad declarada. Que pierdes exactamente frente a Show y Switch.
Que quede claro desde la primera línea: en Solid, el ternario y el operador && sí reaccionan. El compilador rastrea las señales que leen y reevalúa la rama cuando cambian; no están rotos. La pregunta de esta lección es más fina y más útil: cuando ramificas con operadores crudos en lugar de con Show o Switch, ¿qué capacidades pierdes y qué trampa ganas? La respuesta —una fuga de valores falsy, la ausencia de fallback, keyed y accesor estrechado, y la pérdida de intención declarada— explica por qué la comunidad prefiere los componentes de control de flujo salvo en los casos más triviales.
- Ver cómo
&&inserta valores falsy como0o""en el DOM. - Entender que reaccionar no es lo mismo que ofrecer una política de identidad.
- Enumerar lo que pierdes:
fallback,keyed, accesor estrechado, exclusividad. - Saber cuándo un operador crudo es, aun así, la elección correcta.
El operador && inserta el 0
El fallo más concreto y frecuente. a && b en JavaScript no devuelve un booleano: devuelve a si a es falsy, y b si no. Cuando a es 0, "" o NaN, el operador devuelve ese valor falsy, y Solid lo inserta como texto en el DOM:
// BUG clasico: si carrito().length es 0, aparece un "0" suelto en pantalla
<div>{carrito().length && <Badge n={carrito().length} />}</div>
Cuando el carrito está vacío, carrito().length es 0, la expresión completa evalúa a 0, y el usuario ve un 0 fantasma donde esperaba nada. El parche habitual —carrito().length > 0 && ...— funciona, pero es justo eso, un parche que recuerda que el operador no coerciona. Show no tiene este problema por diseño: su prop when acepta T | undefined | null | false y coerciona la verdad, así que un 0 es simplemente falso y se muestra el fallback o nada:
<Show when={carrito().length}>
<Badge n={carrito().length} />
</Show>
flowchart TD A[operador n and Badge] --> B[n vale 0] B --> C[0 and Badge evalua a 0] C --> D[Solid inserta 0 como texto en el DOM] A2[Show con when n] --> B2[coerciona n a booleano] B2 --> E[0 es falso muestra fallback o nada] style D fill:#f38ba8,color:#11111b style E fill:#a6e3a1,color:#11111b
La fuga no se limita a .length. Cualquier expresión que a la izquierda de && produzca 0, "" o NaN termina impresa: precio() && ... escupe un 0 cuando el precio es cero, nombre() && ... no imprime nada solo por suerte —una cadena vacía es falsy y se inserta como texto vacío, pero un 0 numérico se ve—. La lección es que && no coerciona: reenvía el operando izquierdo tal cual, y Solid lo pinta. Show coerciona siempre; el operador, nunca.
Reaccionan, sí, pero no es una política de identidad
El compilador de Solid envuelve los condicionales inline en un memo, así que un ternario simple cond() ? <A /> : <B /> no recrea sus ramas en cada cambio de dependencia: se comporta parecido a Show para el vaivén verdadero↔falso. Hasta aquí, empate aparente. La grieta aparece cuando necesitas algo más que “esto o aquello”:
- Esa memoización es una optimización implícita del compilador, atada a que reconozca sintácticamente el condicional en una posición insertable. En cuanto mueves la condición a una variable, la devuelves desde una función auxiliar o construyes una expresión más enrevesada, la garantía se difumina y puedes acabar recreando el subárbol en cada cambio.
Showhace esa frontera explícita e incondicional: memoiza la verdad siempre, sin depender de la forma del código. - El operador crudo no tiene
equals, no tiene memoria de su decisión previa, no tiene el concepto de “el mismo bloque, otro valor”. Por eso no puede expresarkeyed: no hay forma de decir “recrea este bloque cuando cambie la identidad, aunque siga siendo verdadero”. Esa política de identidad —el corazón de la lección 15.1— sencillamente no existe fuera deShowyMatch.
Reaccionar es el mínimo. Show te da, además, control sobre cómo reacciona: conservar por verdad o recrear por identidad. El ternario te da lo primero y te niega lo segundo.
La fragilidad de esa memoización implícita se ve mejor con código. El compilador solo la aplica cuando reconoce el condicional dentro del JSX:
// bien: condicional inline; el compilador lo envuelve en un memo
<div>{cond() ? <A /> : <B />}</div>
// mal: al sacar la decision del JSX, ni siquiera reacciona
const rama = cond() ? <A /> : <B />; // se evalua UNA vez en el cuerpo
<div>{rama}</div>
El segundo caso es un recordatorio brutal de la lección 6: el cuerpo del componente corre una sola vez, así que rama se calcula una vez y se queda congelada; el ternario ahí no es reactivo en absoluto. El condicional reactivo tiene que vivir dentro del JSX y con la forma exacta que el compilador sabe rastrear. Show, al ser un componente, no depende de esa forma: memoiza siempre, lo pongas donde lo pongas, lo asignes a una variable o lo devuelvas de una función. La reactividad del operador es un privilegio sintáctico; la de Show, un contrato.
El ciclo de creación y destrucción
Volvamos al subtítulo de la lección: cómo Solid crea y destruye. Cuando una condición cambia de rama —da igual si con Show o con un ternario— Solid ejecuta un ciclo preciso: desmonta la rama saliente, corriendo sus onCleanup, cancelando sus efectos, liberando su owner y quitando sus nodos del DOM, y monta la entrante, creando sus nodos, arrancando sus efectos y registrando sus limpiezas. No hay superposición ni reconciliación de árboles: es un relevo limpio.
La diferencia entre las dos herramientas no es si ocurre ese ciclo, sino quién decide cuándo y con qué control. Show ata el ciclo a un memo de la verdad que tú gobiernas: sin keyed, dispara solo al cruzar falso↔verdadero y conserva el subárbol en medio; con keyed, dispara también al cambiar la identidad. Un operador crudo ata el ciclo al memo implícito que el compilador coloca por ti, sin fallback con vida propia, sin keyed y sin accesor estrechado:
// con Show controlas el ciclo: conserva por defecto, resetea con keyed
<Show when={pedido()} keyed>{(p) => <Detalle pedido={p} />}</Show>
// con el ternario obtienes el relevo, pero sin palancas:
// ni fallback nombrado, ni politica de identidad, ni tipo estrechado
{pedido() ? <Detalle pedido={pedido()!} /> : <Vacio />}
Ese es el sentido exacto de “qué pierdes”: no pierdes reactividad ni pierdes el ciclo de creación y destrucción —ambos siguen ahí—, pierdes las palancas para dirigirlo. El operador te da el motor; Show y Switch te dan además el volante.
Lo que pierdes, punto por punto
fallback como lugar nombrado
Con && no hay rama else en absoluto. Con el ternario, el else es una expresión más que anidas; tres estados y ya tienes una pirámide. Show y Switch tienen fallback como ranura con ciclo de vida propio, montada y desmontada como cualquier rama.
keyed y recreacion por identidad
El operador no puede recrear un subárbol al cambiar la identidad del valor mientras la verdad se mantiene. keyed es exclusivo de Show y Match, y con él llega el reseteo limpio de estado entre entidades distintas.
el accesor ya estrechado
user() && <Perfil u={user()!} /> te obliga a leer dos veces y a afirmar no-nulo con !. El hijo-función de Show te entrega un Accessor<NonNullable<T>>: una lectura, tipado estrechado, sin aserciones. Lo verás a fondo en 15.5.
intencion y exclusividad
Un Switch declara “una de estas ramas”; una cadena de ternarios lo implementa por accidente y obliga al lector a demostrar que no se solapan. La forma del código deja de coincidir con la forma del problema.
Ninguna de estas pérdidas es sintáctica; todas son de capacidad. El operador crudo es un condicional de propósito general que resulta reactivo; Show y Switch son condicionales diseñados para el grafo, con lifecycle, política de identidad y estrechamiento de tipos incorporados.
No hay dogma. Para un fragmento minúsculo gobernado por un booleano que ya es booleano —{abierto() && <Chevron />}, {esError() ? "rojo" : "verde"} en un atributo— el operador es breve, claro y sin trampa. La regla práctica: usa el operador cuando la condición sea un booleano genuino, la rama sea trivial, no necesites fallback ni keyed ni estrechar un tipo, y no haya un tercer estado a la vista. En cuanto aparezca cualquiera de esos cuatro, salta a Show o Switch.
La distinción que lo ordena todo es qué está ramificando cada herramienta. && y el ternario son operadores sobre valores: nacieron para elegir entre un número y otro, entre una cadena y otra, y JavaScript los definió con reglas —devolver el operando falsy, cortocircuitar— pensadas para expresiones, no para árboles de UI con estado y ciclo de vida. Que Solid los haga reaccionar es un regalo del compilador, pero siguen siendo operadores de valor a los que les pides, prestados, un trabajo de vista. Show y Switch, en cambio, son operadores sobre vistas: su unidad no es el valor sino el subárbol montado, con su owner, sus efectos, su limpieza y una política explícita de cuándo conservarlo y cuándo destruirlo. Por eso Show coerciona en vez de filtrar, ofrece un fallback con vida propia, expone keyed para decidir la identidad y entrega un accesor estrechado: cada una de esas features es lo que significa “ramificar una vista” en lugar de “ramificar un valor”. Cuando eliges entre un operador y Show, no estás eligiendo entre dos sintaxis del mismo concepto; estás eligiendo el concepto. Ramifica valores con operadores; ramifica vistas con componentes de control de flujo.
- Escribe
{lista().length && <Panel />}con la lista vacía y observa el0fantasma en el DOM. Arréglalo conShow. - Reescribe un ternario anidado de tres estados como
Switchy compara la legibilidad y la garantía de exclusividad. - Toma
usuario() && <Perfil u={usuario()!} />y conviértelo aShowcon hijo-función para eliminar el!y la doble lectura. - Intenta expresar
keyedcon un ternario. Explica por qué es imposible y qué propiedad del operador lo impide. - Identifica en tu código un caso donde el operador crudo sí es correcto y justifica por qué, según los cuatro criterios del callout.