22. Creando componente para los personajes
Tutorial 22: Creando componente para los personajes
🏗️ Paso 1: Crear la estructura de carpetas
Vamos a organizar mejor nuestro proyecto creando una carpeta para componentes:
En VS Code, en el panel izquierdo, haz clic derecho sobre la carpeta src
Selecciona "New Folder" (Nueva carpeta)
Nómbrala Components (con mayúscula inicial, es una convención común)
Presiona Enter
Tu estructura debería verse así:
text
src/
├── Components/ ← ¡Nueva carpeta!
├── img/
├── App.js
├── App.css
└── index.css
📄 Paso 2: Crear el componente Characters
Haz clic derecho sobre la carpeta Components recién creada
Selecciona "New File" (Nuevo archivo)
Nómbralo Characters.js
Presiona Enter
✨ Paso 3: Crear el componente funcional
Dentro del archivo Characters.js, escribe:
javascript
// Characters.js
import React from "react";
export default function Characters() {
return (
<div>
<h1>Personajes</h1>
<p>Aquí se mostrarán todos los personajes...</p>
</div>
);
}
Explicación:
import React from "react": Importamos React (necesario para componentes)
export default function Characters(): Creamos y exportamos el componente
return (): Retornamos el JSX que se renderizará
🔗 Paso 4: Importar el componente en App.js
Ahora vamos a usar nuestro nuevo componente en App.js:
javascript
// En App.js, añade este import junto con los otros:
import Characters from "./Components/Characters";
Tu sección de imports en App.js debería verse así:
javascript
import './App.css';
import imageRickMorty from "./img/rick-morty.png";
import { useState } from "react";
import Characters from "./Components/Characters"; // ← ¡Nuevo import!
🎯 Paso 5: Implementar renderizado condicional
Vamos a modificar el JSX en App.js para mostrar el componente Characters cuando tengamos datos:
javascript
// En App.js, dentro del return:
return (
<div className="App">
<header className="App-header">
<h1 className="title">Rick & Morty</h1>
{/* Renderizado condicional con operador ternario */}
{characters ? (
// Si characters tiene valor (no es null)
<Characters />
) : (
// Si characters es null
<>
<img
src={imageRickMorty}
alt="Rick & Morty"
className="img-home"
/>
<button onClick={reqApi} className="btn-search">
Buscar personajes
</button>
</>
)}
</header>
</div>
);
🔍 Paso 6: Verificar que funciona
Guarda todos los archivos (Ctrl + S en cada uno)
Abre tu navegador en http://localhost:3000
Deberías ver la página inicial normal
Haz clic en "Buscar personajes"
Deberías ver el título "Personajes" y el párrafo de nuestro componente
📦 Paso 7: Pasar datos al componente mediante props
Necesitamos pasar la lista de personajes al componente Characters. Modifica App.js:
javascript
// En App.js, donde usamos el componente:
{characters ? (
<Characters characters={characters} />
) : (
// ... resto del código
)}
🎁 Paso 8: Recibir props en el componente
Ahora actualiza Characters.js para recibir y usar los props:
javascript
// Characters.js
import React from "react";
export default function Characters(props) {
// Extraemos los personajes de las props
const { characters } = props;
console.log("Characters recibidos:", characters);
return (
<div>
<h1>Personajes</h1>
<p>Total: {characters.length} personajes encontrados</p>
{/* Mostrar algunos datos como prueba */}
<div style={{ textAlign: 'left', marginTop: '20px' }}>
<h3>Primeros 3 personajes:</h3>
{characters.slice(0, 3).map((character, index) => (
<div key={index} style={{ marginBottom: '15px', padding: '10px', border: '1px solid white' }}>
<strong>{character.name}</strong>
<br />
<small>Estado: {character.status}</small>
<br />
<small>Especie: {character.species}</small>
</div>
))}
</div>
</div>
);
}
🔄 Paso 9: Pasar también la función para resetear
Vamos a necesitar poder volver a la página de inicio, así que pasamos también la función setCharacters:
javascript
// En App.js:
{characters ? (
<Characters
characters={characters}
setCharacters={setCharacters}
/>
) : (
// ... resto del código
)}
🏠 Paso 10: Agregar funcionalidad para volver
Actualiza Characters.js para incluir un botón para volver:
javascript
// Characters.js
import React from "react";
export default function Characters(props) {
// Extraemos tanto los personajes como la función para resetear
const { characters, setCharacters } = props;
// Función para resetear y volver al inicio
const resetCharacters = () => {
setCharacters(null);
};
console.log("Recibidos:", characters.length, "personajes");
return (
<div className="characters">
<h1>Personajes</h1>
{/* Enlace para volver al inicio */}
<span
onClick={resetCharacters}
style={{
cursor: 'pointer',
color: '#61dafb',
textDecoration: 'underline',
marginBottom: '20px',
display: 'inline-block'
}}
>
← Volver a la home
</span>
<p>Total: {characters.length} personajes</p>
{/* Contenedor para los personajes */}
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: '20px',
marginTop: '20px'
}}>
{characters.map((character, index) => (
<div
key={index}
style={{
padding: '15px',
border: '1px solid #61dafb',
borderRadius: '10px',
textAlign: 'left'
}}
>
<h3>{character.name}</h3>
<p>
<strong>Estado:</strong> {character.status}
</p>
<p>
<strong>Especie:</strong> {character.species}
</p>
<p>
<strong>Episodios:</strong> {character.episode.length}
</p>
{/* Aquí luego añadiremos la imagen */}
</div>
))}
</div>
{/* Otro enlace para volver al final */}
<span
onClick={resetCharacters}
style={{
cursor: 'pointer',
color: '#61dafb',
textDecoration: 'underline',
marginTop: '30px',
display: 'inline-block'
}}
>
← Volver a la home
</span>
</div>
);
}
🎨 Paso 11: Aplicar estilos CSS
Vamos a crear estilos básicos. Primero, añade esto al final de index.css:
css
/* Estilos para el componente Characters */
.characters {
padding: 20px;
width: 100%;
}
.character-card {
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
padding: 15px;
transition: transform 0.3s ease;
}
.character-card:hover {
transform: translateY(-5px);
background: rgba(255, 255, 255, 0.2);
}
.back-home {
color: #61dafb;
text-decoration: underline;
cursor: pointer;
font-size: 16px;
margin: 10px 0;
display: inline-block;
}
.back-home:hover {
color: #21a1f1;
}
Luego actualiza Characters.js para usar estas clases:
javascript
// En Characters.js, actualiza el return:
return (
<div className="characters">
<h1>Personajes</h1>
<span className="back-home" onClick={resetCharacters}>
← Volver a la home
</span>
<div className="container-characters">
{characters.map((character, index) => (
<div className="character-card" key={index}>
<h3>{character.name}</h3>
<p><strong>Estado:</strong> {character.status}</p>
<p><strong>Especie:</strong> {character.species}</p>
<p><strong>Episodios:</strong> {character.episode.length}</p>
<p><strong>Género:</strong> {character.gender}</p>
</div>
))}
</div>
<span className="back-home" onClick={resetCharacters}>
← Volver a la home
</span>
</div>
);
📝 Código completo de App.js hasta ahora:
javascript
import './App.css';
import imageRickMorty from "./img/rick-morty.png";
import { useState } from "react";
import Characters from "./Components/Characters";
function App() {
const [characters, setCharacters] = useState(null);
const reqApi = async () => {
try {
const api = await fetch("https://rickandmortyapi.com/api/character");
if (!api.ok) {
throw new Error(`Error: ${api.status}`);
}
const charactersApi = await api.json();
setCharacters(charactersApi.results);
} catch (error) {
console.error("Error:", error);
alert("Error al cargar personajes");
}
};
return (
<div className="App">
<header className="App-header">
<h1 className="title">Rick & Morty</h1>
{/* RENDERIZADO CONDICIONAL */}
{characters ? (
<Characters
characters={characters}
setCharacters={setCharacters}
/>
) : (
<>
<img
src={imageRickMorty}
alt="Rick & Morty"
className="img-home"
/>
<button onClick={reqApi} className="btn-search">
Buscar personajes
</button>
</>
)}
</header>
</div>
);
}
export default App;
📝 Código completo de Characters.js hasta ahora:
javascript
import React from "react";
export default function Characters(props) {
const { characters, setCharacters } = props;
const resetCharacters = () => {
setCharacters(null);
};
console.log("Mostrando", characters.length, "personajes");
return (
<div className="characters">
<h1>Personajes</h1>
<span className="back-home" onClick={resetCharacters}>
← Volver a la home
</span>
<div className="container-characters">
{characters.map((character, index) => (
<div className="character-card" key={index}>
<h3>{character.name}</h3>
<p><strong>Estado:</strong> {character.status}</p>
<p><strong>Especie:</strong> {character.species}</p>
<p><strong>Episodios:</strong> {character.episode.length}</p>
<p><strong>Origen:</strong> {character.origin.name}</p>
</div>
))}
</div>
<span className="back-home" onClick={resetCharacters}>
← Volver a la home
</span>
</div>
);
}
🎯 Paso 12: Verificación final
Prueba el flujo completo:
Página inicial: Imagen + botón
Haz clic en "Buscar personajes": Deberías ver 20 tarjetas con personajes
Haz clic en "Volver a la home": Deberías regresar a la página inicial
Verifica en consola: Deberías ver el log con la cantidad de personajes
🔧 Solucionando problemas comunes:
Error: "Characters is not defined"
javascript
// Verifica que el import sea correcto:
import Characters from "./Components/Characters"; // Con mayúscula
// No: import characters from "./Components/Characters";
Error: "characters.map is not a function"
javascript
// Asegúrate de que characters sea un array
console.log("Tipo de characters:", typeof characters);
console.log("Es array?", Array.isArray(characters));
Error: "Cannot read properties of null"
javascript
// El renderizado condicional debería evitar esto
{characters ? <Characters characters={characters} /> : /* home */}
🎉 ¡Felicidades!
Has creado tu primer componente reutilizable en React. Has logrado:
✅ Organizar el proyecto con carpeta Components
✅ Crear un componente funcional
✅ Pasar datos mediante props
✅ Implementar renderizado condicional
✅ Crear flujo bidireccional (ir y volver)
✅ Aplicar estilos básicos
🚀 Próximo paso: Mostrar imágenes y mejorar diseño
En el siguiente tutorial vamos a:
Mostrar las imágenes de cada personaje
Mejorar el diseño con CSS Grid
Agregar indicadores visuales para estado (vivo/muerto)
Hacer el diseño responsive
🧪 Actividad práctica
Modifica tu componente para:
Mostrar solo personajes que estén vivos (status === "Alive")
Ordenar alfabéticamente por nombre
Agregar un contador de personajes por especie
javascript
// Ejemplo: Filtrar solo vivos
const aliveCharacters = characters.filter(char => char.status === "Alive");
// Usar en el map:
{aliveCharacters.map((character, index) => (
// ... tu código
))}
¿Cuántos personajes vivos vs muertos hay en la primera página? ¡Compártelo! 🌟
¿Tienes dudas sobre cómo funcionan los props o el renderizado condicional? ¡Pregunta en los comentarios
Comentarios
Publicar un comentario