24. Recorriendo los datos que vienen de la API
Tutorial 24: Recorriendo los datos que vienen de la API
馃殌 Introducci贸n al manejo de arrays en React
¡Perfecto! Ya tenemos nuestra aplicaci贸n funcionando con datos reales de la API. Ahora vamos a recorrer y mostrar todos los personajes de forma organizada. Este es el coraz贸n de nuestra aplicaci贸n.
馃摝 Paso 1: Preparar nuestro componente Characters.js
Vamos a empezar con la estructura base del componente:
javascript
// Characters.js
import React from "react";
export default function Characters(props) {
// 1. Destructuring para extraer characters de las props
const { characters } = props;
// 2. Verificar en consola lo que recibimos
console.log("Datos recibidos:", characters);
console.log("Cantidad de personajes:", characters.length);
return (
<div className="characters">
<h1>Personajes</h1>
{/* Aqu铆 recorreremos los datos */}
</div>
);
}
馃幆 Paso 2: Destructuring de props
El destructuring nos permite extraer propiedades de objetos de forma m谩s limpia:
javascript
// Opci贸n 1: Destructuring en la funci贸n
export default function Characters({ characters }) {
// Ahora characters est谩 disponible directamente
console.log(characters);
}
// Opci贸n 2: Destructuring dentro de la funci贸n
export default function Characters(props) {
const { characters } = props;
// characters est谩 disponible aqu铆
}
// Opci贸n 3: Tambi茅n podemos extraer setCharacters si lo necesitamos
export default function Characters({ characters, setCharacters }) {
// Tenemos ambos disponibles
}
馃攧 Paso 3: Usar .map() para recorrer el array
El m茅todo .map() es esencial en React para transformar arrays de datos en elementos JSX:
javascript
export default function Characters({ characters }) {
return (
<div className="characters">
<h1>Personajes</h1>
{/* Contenedor principal */}
<div className="container-characters">
{/* .map() para recorrer todos los personajes */}
{characters.map((character, index) => (
// Este return es impl铆cito gracias a los par茅ntesis
<div className="character-container" key={index}>
<p>{character.name}</p>
</div>
))}
</div>
</div>
);
}
馃攽 Paso 4: Entender el return impl铆cito
Cuando usamos par茅ntesis () despu茅s de la flecha, el return es impl铆cito:
javascript
// RETURN IMPL脥CITO (con par茅ntesis)
characters.map((character, index) => (
<div key={index}>
{character.name}
</div>
))
// RETURN EXPL脥CITO (con llaves)
characters.map((character, index) => {
return (
<div key={index}>
{character.name}
</div>
)
})
// Ambos hacen lo mismo, pero el impl铆cito es m谩s conciso
馃彈️ Paso 5: Estructurar cada tarjeta de personaje
Vamos a crear una estructura completa para cada personaje:
javascript
export default function Characters({ characters }) {
return (
<div className="characters">
<h1>Personajes</h1>
{/* Enlace para volver (lo implementaremos despu茅s) */}
<span className="back-home">
Volver a la home
</span>
{/* Contenedor grid para todos los personajes */}
<div className="container-characters">
{characters.map((character, index) => (
<div className="character-container" key={index}>
{/* Contenedor para la imagen */}
<div>
<img
src={character.image}
alt={character.name}
/>
</div>
{/* Contenedor para la informaci贸n */}
<div>
<h3>{character.name}</h3>
{/* Estado (vivo/muerto) */}
<h6>
{character.status === "Alive" ? (
<>
<span className="alive" />
Alive
</>
) : (
<>
<span className="dead" />
Dead
</>
)}
</h6>
{/* Episodios */}
<p>
<span className="text-grey">Episodios: </span>
<span>{character.episode.length}</span>
</p>
{/* Especie */}
<p>
<span className="text-grey">Especie: </span>
<span>{character.species}</span>
</p>
{/* G茅nero */}
<p>
<span className="text-grey">G茅nero: </span>
<span>{character.gender}</span>
</p>
{/* Origen */}
<p>
<span className="text-grey">Origen: </span>
<span>{character.origin.name}</span>
</p>
</div>
</div>
))}
</div>
{/* Otro enlace para volver al final */}
<span className="back-home">
Volver a la home
</span>
</div>
);
}
馃帹 Paso 6: Agregar la funcionalidad para volver
Necesitamos recibir setCharacters para poder resetear el estado:
javascript
export default function Characters(props) {
// Extraemos ambos valores
const { characters, setCharacters } = props;
// Funci贸n para resetear
const resetCharacters = () => {
setCharacters(null);
};
return (
<div className="characters">
<h1>Personajes</h1>
{/* Enlace funcional para volver */}
<span className="back-home" onClick={resetCharacters}>
Volver a la home
</span>
{/* Resto del c贸digo igual... */}
</div>
);
}
No olvides actualizar App.js:
javascript
// En App.js, pasa setCharacters tambi茅n
<Characters
characters={characters}
setCharacters={setCharacters}
/>
馃摑 Paso 7: C贸digo completo mejorado
javascript
// Characters.js - Versi贸n completa
import React from "react";
export default function Characters(props) {
// Destructuring de props
const { characters, setCharacters } = props;
// Funci贸n para volver al inicio
const resetCharacters = () => {
setCharacters(null);
};
// Verificaci贸n de datos (buena pr谩ctica)
if (!characters || characters.length === 0) {
return (
<div className="characters">
<h1>No hay personajes para mostrar</h1>
<span className="back-home" onClick={resetCharacters}>
Volver a la home
</span>
</div>
);
}
return (
<div className="characters">
<h1>Personajes ({characters.length})</h1>
<span className="back-home" onClick={resetCharacters}>
← Volver a la home
</span>
<div className="container-characters">
{characters.map((character, index) => (
<div className="character-container" key={index}>
{/* Secci贸n de imagen */}
<div className="character-image">
<img
src={character.image}
alt={character.name}
loading="lazy" // Mejora performance
/>
</div>
{/* Secci贸n de informaci贸n */}
<div className="character-info">
<h3>{character.name}</h3>
{/* Estado con indicador visual */}
<div className="character-status">
{character.status === "Alive" ? (
<span className="status-alive">
● Alive
</span>
) : character.status === "Dead" ? (
<span className="status-dead">
● Dead
</span>
) : (
<span className="status-unknown">
● Unknown
</span>
)}
</div>
{/* Lista de informaci贸n */}
<div className="character-details">
<p>
<span className="label">Especie:</span>
<span className="value">{character.species}</span>
</p>
<p>
<span className="label">G茅nero:</span>
<span className="value">{character.gender}</span>
</p>
<p>
<span className="label">Episodios:</span>
<span className="value">{character.episode.length}</span>
</p>
<p>
<span className="label">Origen:</span>
<span className="value">{character.origin.name}</span>
</p>
<p>
<span className="label">Ubicaci贸n:</span>
<span className="value">{character.location.name}</span>
</p>
</div>
</div>
</div>
))}
</div>
<span className="back-home" onClick={resetCharacters}>
← Volver a la home
</span>
</div>
);
}
馃帹 Paso 8: Agregar estilos CSS esenciales
A帽ade estos estilos a tu index.css:
css
/* Estilos para Characters */
.characters {
width: 100%;
max-width: 1400px;
padding: 20px;
}
.container-characters {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 25px;
margin: 30px 0;
}
.character-container {
background: #3c3e44;
border-radius: 10px;
overflow: hidden;
display: flex;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.character-container:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
}
.character-image {
width: 40%;
min-width: 150px;
}
.character-image img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.character-info {
width: 60%;
padding: 15px;
color: white;
}
.character-info h3 {
margin: 0 0 10px 0;
color: #61dafb;
font-size: 1.3rem;
}
.character-status {
margin-bottom: 15px;
font-size: 0.9rem;
}
.status-alive {
color: #55cc44;
}
.status-dead {
color: #d63d2e;
}
.status-unknown {
color: #9e9e9e;
}
.character-details {
font-size: 0.9rem;
}
.character-details p {
margin: 8px 0;
display: flex;
justify-content: space-between;
}
.character-details .label {
color: #9e9e9e;
font-weight: bold;
}
.character-details .value {
color: white;
text-align: right;
max-width: 60%;
}
.back-home {
color: #61dafb;
text-decoration: underline;
cursor: pointer;
font-size: 16px;
margin: 10px 0;
display: inline-block;
}
.back-home:hover {
color: #21a1f1;
}
/* Responsive */
@media (max-width: 768px) {
.container-characters {
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 15px;
}
.character-container {
flex-direction: column;
}
.character-image,
.character-info {
width: 100%;
}
.character-image {
height: 200px;
}
.character-image img {
height: 100%;
}
}
@media (max-width: 480px) {
.container-characters {
grid-template-columns: 1fr;
}
.characters {
padding: 10px;
}
}
馃敡 Paso 9: Verificaci贸n y debugging
Agrega estos console.log para verificar que todo funciona:
javascript
export default function Characters({ characters, setCharacters }) {
// Verificar datos
console.log("=== DEBUG ===");
console.log("Tipo de characters:", typeof characters);
console.log("Es array?:", Array.isArray(characters));
console.log("Longitud:", characters?.length || 0);
// Verificar un personaje espec铆fico
if (characters && characters.length > 0) {
console.log("Primer personaje:", characters[0]);
console.log("Tiene imagen?:", characters[0].image);
console.log("Tiene nombre?:", characters[0].name);
}
// Resto del c贸digo...
}
馃幆 Paso 10: Probar el recorrido completo
Prueba estos aspectos:
¿Se muestran todas las im谩genes?
¿Los nombres aparecen correctamente?
¿Los estados (vivo/muerto) se muestran?
¿La informaci贸n adicional es visible?
¿El dise帽o es responsive?
馃挕 Consejos para trabajar con .map()
javascript
// 1. SIEMPRE usa key 煤nica
characters.map((character, index) => (
<div key={character.id}> // Mejor usar id si existe
{character.name}
</div>
))
// 2. Puedes filtrar antes de mapear
characters
.filter(char => char.status === "Alive")
.map((character, index) => (
// Solo personajes vivos
))
// 3. Puedes ordenar antes de mapear
characters
.sort((a, b) => a.name.localeCompare(b.name))
.map((character, index) => (
// Orden alfab茅tico
))
馃帀 ¡Felicidades!
Has logrado recorrer y mostrar datos complejos de una API. Ahora tienes:
✅ Componente funcional que recibe props
✅ Destructuring para acceder a datos f谩cilmente
✅ .map() para recorrer arrays de datos
✅ Return impl铆cito para c贸digo m谩s limpio
✅ Estructura HTML sem谩ntica para cada personaje
✅ Estilos CSS organizados y responsive
✅ Funcionalidad completa de ida y vuelta
馃殌 Pr贸ximo paso: Mejorar la UI/UX
En el siguiente tutorial vamos a:
Agregar efectos hover y transiciones
Mejorar los indicadores de estado (colores, 铆conos)
Agregar loading states mientras se cargan datos
Implementar manejo de errores mejorado
馃И Actividad pr谩ctica
Modifica tu c贸digo para:
Mostrar solo personajes de una especie espec铆fica (ej: "Human")
Agregar un buscador por nombre
Implementar paginaci贸n (cargar m谩s personajes)
javascript
// Ejemplo: Filtrar por especie humana
const humanCharacters = characters.filter(char =>
char.species.toLowerCase() === "human"
);
// Usar en el map:
{humanCharacters.map((character, index) => (
// Tu c贸digo aqu铆
))}
¿Cu谩ntos personajes humanos vs aliens hay en tu lista? ¡Comp谩rtelo! 馃専
¿Tienes preguntas sobre c贸mo funciona .map() o el destructuring? ¡Es el momento perfecto para aclararlas
Comentarios
Publicar un comentario