Show: when, fallback y keyed
Show es el condicional reactivo de Solid. Renderiza sus hijos cuando when es verdadero y el fallback cuando no, memoiza la coercion booleana para no recrear en vano y, con keyed, recrea el subarbol cuando cambia la identidad del valor.
Show es el if de un lenguaje reactivo, pero reducirlo a “un ternario con mejor sintaxis” es perder lo esencial. Show hace tres cosas que un operador crudo no hace: memoiza la coerción booleana de when para no recrear a sus hijos cuando la condición cambia sin cambiar de verdad, ofrece una ranura fallback para el caso falso, y con el modificador keyed invierte su propia semántica para recrear el subárbol cuando cambia la identidad del valor. Entender esos tres comportamientos es entender el condicional de Solid.
- Leer la firma de
Show: la propwhen, la propfallbacky los hijos. - Entender que
Showmemoiza la coerción booleana dewhen. - Distinguir cuándo
Showconserva los hijos y cuándo los recrea. - Usar
keyedpara forzar la recreación al cambiar la identidad del valor.
La firma: when, fallback e hijos
Show recibe una prop when y unos hijos; renderiza los hijos cuando when es truthy y, si no, renderiza la prop fallback —que por defecto es nada—:
import { Show } from "solid-js";
function Panel(props: { cargando: boolean; datos: Datos }) {
return (
<Show when={!props.cargando} fallback={<Spinner />}>
<Vista datos={props.datos} />
</Show>
);
}
when no exige un booleano estricto: cualquier valor truthy activa los hijos y cualquier valor falsy —false, null, undefined, 0, "", NaN— activa el fallback. El tipo lo dice literalmente: when es T | undefined | null | false. Esa coerción es deliberada y es justo lo que convierte a Show en el guardián de nulos idóneo, tema que retomaremos en la última lección de este nivel. Como todo control de flujo de Solid, Show vive dentro del JSX: el compilador rastrea la lectura de when y reevalúa la rama cuando su valor cambia, sin volver a ejecutar el cuerpo del componente. Y fallback es tan reactivo como los hijos: puede ser un componente con su propio estado, no solo un texto muerto.
La condición se memoiza: por qué no recrea
Aquí está la propiedad que separa a Show de un &&. Internamente, Show envuelve when en un memo cuyo equals compara la verdad booleana, no el valor: en esencia (a, b) => !a === !b. Por eso el bloque de hijos solo se recalcula cuando when cruza la frontera falso↔verdadero. Mientras when siga siendo truthy, aunque su valor interno cambie de un truthy a otro, los hijos no se recrean:
const [texto, setTexto] = createSignal("hola");
// el Editor se crea UNA vez cuando texto() pasa a no-vacio,
// y conserva su estado interno al pasar de "hola" a "adios"
<Show when={texto()}>
<Editor />
</Show>
La consecuencia es enorme y silenciosa: el estado interno del subárbol —el valor de un input, el foco, el scroll, un createEffect a medias— sobrevive a los cambios de when que no alteran su verdad. Compruébalo con un caso tangible: un campo no controlado dentro del Show mantiene lo que el usuario escribió aunque la condición pase de un truthy a otro truthy.
const [rol, setRol] = createSignal("editor");
// al cambiar rol de "editor" a "revisor", ambos truthy,
// el input NO se vacia: Show conserva el subarbol
<Show when={rol()}>
<input placeholder="tu nota" />
</Show>
La documentación lo enuncia sin rodeos: sustituir un valor truthy por otro truthy no recrea el bloque hijo. Un ternario crudo no te ofrece esa garantía como capacidad nombrada; Show la hace explícita e incondicional.
flowchart TD W[when cambia de valor] --> M[memo de la condicion equals por verdad] M -->|misma verdad booleana| K[conserva hijos estado y DOM] M -->|pasa a verdadero| C[crea los hijos una sola vez] M -->|pasa a falso| F[muestra el fallback] style K fill:#a6e3a1,color:#11111b style C fill:#89b4fa,color:#11111b style F fill:#fab387,color:#11111b
El fallback no se construye mientras when sea verdadero, y los hijos no se construyen mientras sea falso. Show materializa una sola de las dos ramas a la vez y desecha la otra; no hay coste oculto por la rama inactiva porque, sencillamente, no existe en el DOM. Esto es lo contrario de un render que evalúa todo y luego oculta con CSS.
keyed: recrear al cambiar la identidad
A veces conservar el subárbol es justo lo que no quieres. Si pasas de editar el usuario A al usuario B, un formulario que conserva el borrador de A es un bug. Para eso está keyed: cambia el equals interno a una comparación por identidad —en esencia (a, b) => a === b—, de modo que cualquier cambio de referencia de when, aunque siga siendo truthy, recrea el bloque entero desde cero:
// sin keyed: el subarbol persiste; solo los bindings finos se actualizan
<Show when={usuario()} fallback={<Anonimo />}>
<Perfil />
</Show>
// con keyed: cada usuario nuevo destruye y reconstruye Perfil
<Show when={usuario()} keyed fallback={<Anonimo />}>
{(u) => <Perfil id={u.id} />}
</Show>
Fíjate en el segundo detalle: en la forma keyed, el hijo-función recibe el valor ya estrechado directamente (u es NonNullable<T>), no un accesor. Tiene sentido, porque el bloque está atado a la vida de ese valor concreto: cuando el valor cambia, el bloque muere y nace otro con el nuevo u. En la forma sin keyed, en cambio, el hijo-función recibe un accesor que mantiene vivo el subárbol; esa es la forma guard que diseccionaremos en la lección 15.5.
Activar keyed altera la política de recreación y el tipo del hijo-función. Sin keyed: se conserva el subárbol y el callback recibe Accessor<NonNullable<T>>. Con keyed: se recrea el subárbol en cada cambio de identidad y el callback recibe NonNullable<T>. No son dos features independientes: son las dos caras de “¿este bloque está ligado a la verdad de la condición o a la identidad del valor?”.
Elegir la política: conservar o recrear
La pregunta que decide entre poner o quitar keyed no es de rendimiento, es de corrección: ¿qué debe pasarle al estado del subárbol cuando el valor cambia sin volverse nulo?
Sin keyed: conservar por verdad
El subárbol persiste mientras when sea truthy. Ideal cuando el contenido representa lo mismo con datos frescos: un panel que muestra el pedido actual, un editor cuyo texto no debe perderse, una vista cuyo scroll importa. El estado sobrevive; los bindings se refrescan.
Con keyed: recrear por identidad
El subárbol se destruye y reconstruye cuando cambia la referencia. Ideal cuando el valor nuevo es otra entidad: cambiar de usuario, de documento, de sesión. Nada del anterior debe filtrarse; keyed garantiza un lienzo limpio en cada cambio.
Un criterio operativo: si dudas, empieza sin keyed. Conservar es el comportamiento seguro por defecto —evita trabajo y preserva estado— y solo cuando detectes contaminación entre valores distintos (un borrador que se arrastra, un efecto que no se reinicia) añades keyed para forzar el reseteo. Poner keyed “por si acaso” desperdicia recreaciones que no necesitabas.
La tentación al llegar de React es leer Show como un if con estilo. Es un error de categoría. Show es un nodo del grafo reactivo con una decisión de diseño incrustada: su equals define qué significa “cambió la condición”. Por defecto, cambiar significa cruzar la frontera booleana, y entonces conservar el subárbol maximiza la preservación de estado y minimiza el trabajo del DOM —es lo que quieres el noventa por ciento del tiempo—. Con keyed, cambiar significa cambiar de identidad, y entonces recrear garantiza que ningún estado del valor anterior contamine al nuevo. Esa elección —conservar por verdad o recrear por identidad— no es expresable con un && ni con un ternario, porque esos operadores no tienen equals, no tienen memoria de su decisión previa, no tienen concepto de “el mismo bloque, otro valor”. Show sí. Cuando internalices que estás eligiendo una política de identidad cada vez que decides poner o quitar keyed, dejarás de ver Show como sintaxis y empezarás a verlo como lo que es: el punto donde declaras cómo debe reaccionar tu UI a la diferencia entre “otro valor” y “otro estado del mundo”. En el núcleo de Solid 2.0 la evaluación se vuelve más perezosa, pero este contrato —verdad conserva, identidad recrea— se mantiene intacto.
- Monta un
Showsinkeyedalrededor de un<input>no controlado; escribe algo, cambiawhende un truthy a otro truthy y confirma que el texto delinputsobrevive. - Añade
keyedy repite: comprueba que ahora elinputse vacía en cada cambio de identidad. - Explica, en términos del
equalsinterno, por qué el paso 1 conserva y el paso 2 recrea. - Usa un
fallbackcon estado propio y demuestra que la rama inactiva no aparece en el DOM (inspecciona el árbol, no usesdisplay:none). - Con la forma
keyedy hijo-función, imprime elidrecibido y verifica que llega el valor directo, no un accesor que haya que invocar.