Ir al contenido
wannacode

Condiciones: vacío, error, cargando

En resumen: El renderizado condicional muestra solo la parte del marcado que corresponde a los datos (cargando, error, vacío o la lista); en React con && y el operador ternario.

Problema

Cargando, error y la lista a la vez

El catálogo se ha cargado. Pero bajo el encabezado dice «Cargando…», luego «Los productos no se cargaron.», luego «No hay productos en esta categoría.», y solo después vienen los productos.

React

El componente Catalog recibe status y los productos como props. Pero no los mira: siempre dibuja las cuatro partes. El comprador no sabe qué creer, y un lector de pantalla lee tanto el error como los productos.

La idea

La parte que corresponde a los datos

El catálogo tiene cuatro estados: cargando, error, vacío y listo. En la pantalla debe estar solo la parte del estado actual. Esto es el renderizado condicional: el componente decide qué devolver según los datos.

JSX tiene tres maneras, según cuánto cambie:

// Una vista completamente distinta: if y un return temprano
if (status === 'loading') return <p role="status">Cargando…</p>;
// Una de dos: ? :
<p>{product.stock === 0 ? 'Agotado' : 'Disponible'}</p>
// Algo o nada: &&
{status === 'error' && <p role="alert">Los productos no se cargaron.</p>}

if es JavaScript normal: el componente es una función y puede devolver distinto JSX. ? : y && son expresiones, así que pueden estar dentro de JSX, en {}.

Las dos bibliotecas resuelven lo mismo a su manera: React con JavaScript alrededor del marcado, Vue con atributos dentro. El resultado es el mismo: el elemento de una condición falsa no está en el DOM. No está oculto, no existe.

Aquí status se pasa como prop. Cómo lo cambia el propio catálogo mientras carga desde la API está en el módulo «Efectos, datos y formularios».

Míralo

Ver qué rama entra

Un valor entra en el componente, y solo una rama entra en el DOM. Fíjate en la columna «DOM» en cada cambio de status.

function Catalog({ status, products }) {
if (status === 'loading') return <p>Cargando…</p>;
if (status === 'error') return <p>Error</p>;
if (status === 'empty') return <p>No hay productos</p>;
return <ul>…</ul>;
}
// en el DOM, pero oculto
<p hidden={status !== 'loading'}>Cargando…</p>
Paso a paso, en palabras
  1. status llega al componente como prop. Es 'loading': el primer if coincide, y el componente devuelve solo <p>Cargando…</p>. En el DOM entra solo eso.
  2. status pasa a 'error'. La biblioteca dibuja otra vez: ahora coincide la segunda rama. <p>Cargando…</p> sale del DOM, y entra el mensaje de error.
  3. 'empty': solo el mensaje de que la categoría está vacía.
  4. 'ready': ningún if coincide, y el componente llega al último return: la lista. En el DOM hay siempre una rama.
  5. La otra manera: el elemento se dibuja siempre, y el atributo hidden decide si se ve. Con loading, hidden es false y el texto se ve.
  6. status pasa a 'ready'. El elemento se queda en el DOM, con todo lo que tiene dentro, y solo recibe display: none. Nadie lo ve, y un lector de pantalla lo salta.
  7. Una condición con if, ? : o && quita el elemento del DOM. hidden solo lo oculta. Quitar es lo habitual; ocultar es para algo que se cambia a menudo o que debe conservar lo escrito.

La primera parte es el renderizado condicional: con un valor nuevo, el elemento viejo sale del DOM y entra el nuevo. La segunda parte es ocultar: el elemento está en el DOM todo el tiempo y solo se muestra o se oculta. De eso trata el final de la lección.

Pruébalo

Los cuatro estados

Catalog dibuja todo a la vez. Haz que cada sección tenga solo la parte de su estado: «Cargando…» para cargando, el error para error, «No hay productos en esta categoría.» para vacío y la lista para listo. Las otras partes no deben estar en la página.

React

Pruébalo

Completa el hueco

«Cargando…» debe aparecer solo mientras status es loading.

{status === 'loading'  <p role="status">Cargando…</p>}

La idea

Agotado: una insignia y un botón desactivado

Una condición no es solo para partes enteras. En la tarjeta del producto cambia un texto y un atributo:

{product.stock === 0 ? (
<p className="badge">Agotado</p>
) : (
<p>Quedan {product.stock} uds.</p>
)}
<button type="button" disabled={product.stock === 0}>Añadir</button>

disabled={true} pone el atributo disabled, y disabled={false} lo quita.

Un botón desactivado no se puede pulsar, no recibe el foco con Tab, y un lector de pantalla dice que no está disponible. La insignia dice por qué. Un botón que se puede pulsar y luego dice «sin existencias» es peor: el comprador se entera solo después del clic.

Pruébalo

Agotado

Haz que ProductCard muestre «Agotado» y desactive el botón «Añadir» cuando stock sea 0. Si no, muestra «Quedan 5 uds.» y el botón funciona. El queso está agotado.

React

La idea

El cero en la pantalla

&& no devuelve true o false. Devuelve el lado izquierdo si es falsy, y el lado derecho si es truthy. React dibuja los números, el 0 incluido, y no dibuja false, null ni undefined. Así que:

{items.length && <p>En el carrito: {items.length}</p>} // carrito vacío: 0
{items.length > 0 && <p>En el carrito: {items.length}</p>} // carrito vacío: nada

Antes de && pon una comparación que dé true o false: > 0, === 0, !== null. Otra manera es ? : con null para «nada».

Pruébalo

Encuentra el error

Con el carrito vacío, en la esquina de arriba pone «0».

Elige la línea con el error.

La idea

Quitado u oculto

A veces un elemento tiene que quedarse en el DOM y solo no verse. Aquí los dos paneles tienen un campo para una nota. Abre los dos, escribe algo, ciérralos y ábrelos otra vez.

React
En la primera sección el panel usa {removedOpen && …}: cerrado desaparece, y abierto es nuevo, con el campo vacío. En la segunda usa el atributo hidden={!hiddenOpen}: se queda en el DOM con display: none y conserva lo escrito.

Quitar es lo habitual: menos elementos en la página, nada oculto para un lector de pantalla. Ocultar es para partes que se abren y se cierran a menudo o tienen que conservar lo que se introdujo en ellas.

Más precisamente, para curiosos

hidden y CSS. hidden funciona mediante display: none de los estilos del navegador. Si una regla de CSS da al elemento display: flex, gana la regla y el elemento sigue visible. Entonces lo ocultas con una clase o con style={{ display: 'none' }}.

Mismo lugar, mismo elemento. Con {isSearch ? <input placeholder="Buscar" /> : <input placeholder="Código" />} cada rama es un <input> en el mismo lugar. React conserva el elemento y solo cambia placeholder, y lo escrito se queda. Si necesitas dos elementos distintos, dales key distintas.

return null. Un componente que no debe dibujar nada devuelve null.

Los componentes de clase del código antiguo hacen lo mismo en el método render().

aria-hidden no es ocultar. aria-hidden="true" oculta el elemento solo de un lector de pantalla, y se queda en la pantalla. Ocultar para todos es display: none: hidden, v-show o quitar.

Ejercicio

El carrito

App muestra tres carritos: vacío, con productos y con un producto agotado. Haz Cart de modo que el vacío diga «El carrito está vacío.», sin lista y sin total, y el botón «Hacer el pedido» esté desactivado. El lleno muestra las filas, «Total: 4,00 €» y un botón que funciona. El producto agotado tiene una insignia «Agotado», encima del botón dice «Quita los productos agotados para hacer el pedido.» (rol alert), y el botón está desactivado.

React

Test

Ponte a prueba

1. La condición de «Cargando…» es falsa. ¿Qué hay en su lugar en el DOM?
2. El carrito está vacío. ¿Qué dibuja {items.length && <p>En el carrito: {items.length}</p>}?
3. ¿Qué encaja mejor para que el componente muestre solo «Cargando…» mientras status es loading?
4. Un panel con un campo de nota se abre y se cierra a menudo, y lo escrito tiene que quedarse. ¿Qué encaja mejor?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·