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:

  1. En VS Code, en el panel izquierdo, haz clic derecho sobre la carpeta src

  2. Selecciona "New Folder" (Nueva carpeta)

  3. Nómbrala Components (con mayúscula inicial, es una convención común)

  4. 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

  1. Haz clic derecho sobre la carpeta Components recién creada

  2. Selecciona "New File" (Nuevo archivo)

  3. Nómbralo Characters.js

  4. 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

  1. Guarda todos los archivos (Ctrl + S en cada uno)

  2. Abre tu navegador en http://localhost:3000

  3. Deberías ver la página inicial normal

  4. Haz clic en "Buscar personajes"

  5. 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:

  1. Página inicial: Imagen + botón

  2. Haz clic en "Buscar personajes": Deberías ver 20 tarjetas con personajes

  3. Haz clic en "Volver a la home": Deberías regresar a la página inicial

  4. 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:

  1. Mostrar las imágenes de cada personaje

  2. Mejorar el diseño con CSS Grid

  3. Agregar indicadores visuales para estado (vivo/muerto)

  4. Hacer el diseño responsive


🧪 Actividad práctica

Modifica tu componente para:

  1. Mostrar solo personajes que estén vivos (status === "Alive")

  2. Ordenar alfabéticamente por nombre

  3. 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

Entradas más populares de este blog

9.3-Tutorial de Componentes React con Props -SSS

9.6-Ciclo de Vida de un Componente React

9-Componentes en React-estado