Franco Figueroa Flores
Gabriel Esquivel
Rafael Soriano Ramírez
Hansell Kopp
Henry J. Perez
enderson omaña
Geovanni Labastida Vázquez
Juan Camilo Lentino Villalba
Geovanni Labastida Vázquez
Andres Prieto
Stevenson Grajales
Max Andy Diaz Neyra
Zaidibeth Ramos
Andrés Felipe Eslava Zuluaga
Fabian Siatama
José de Jesús Valera Durán
David Rangel
Gustavo Quispe Ramos
Juan Diego Mejia Maestre
Harold Zurita Simon
CRISTIAN ANDRES ALVAREZ YAYA
Sebastian Carballo
Nazareno Rosales
Adjunto mi respuesta
genial, no sabía que existía ese join
Muy bien sintetizado,
Seria un poco mejor pasar la logica de consulta de los detalles a al componente de PokemonCard. Eso evitaria el uso de Promise.all() el paso excesivo de parametros del componente padre al hijo y ademas seria un poco mas eficiente
Promise.all()
Es un metodo estatico de la clase Promise que toma como input un array de promesas y devuelve como resultado un array con los resultados de las promesas resueltas en el orden en que fueron agregadas en el input.
Devuelve los resultados una vez todas las promesas sean resueltas. Si una de ellas falla el Promise.all rechazara la operación.
Este ejemplo lo saque del confiable MDN Web Docs, por lo que si quieres una explicación más a fondo ve para alla:
const promise1 = Promise.resolve(3); const promise2 = 42; const promise3 = new Promise((resolve, reject) => { setTimeout(resolve, 100, 'foo'); }); Promise.all([promise1, promise2, promise3]).then((values) => { console.log(values); }); // expected output: Array [3, 42, "foo"]
Adjunto mi respuesta ingresando todos los datos y haciendo uso del método join
A alguien más les aparecio este Warning en Consola? .
Application state or actions payloads are too large making Redux DevTools serialization slow and consuming a lot of memory. See https://github.com/reduxjs/redux-devtools-extension/blob/master/docs/Troubleshooting.md#excessive-use-of-memory-and-cpu on how to configure it.
Para el desafío, desde el componente PokemonList pase las habilidades por props a el componente PokemonCard
const PokemonList = ({ pokemons })=> { return ( <div className="PokemonList"> { pokemons.map(pokemon => ( <PokemonCard name={pokemon.name} key={pokemon.name} image={pokemon.sprites.front_default} abilities={pokemon.abilities} /> // ** )) } </div> ) }
desde el componente PokemonCard recibí y desestructure todas las props, al ser un arreglo lo que recibe en la prop de las habilidades, cree la lógica para poder iterar sobre cada elemento de este arreglo y presentar el nombre de cada habilidad en forma de lista de manera dinámica
const PokemonCard = ({ name, image, abilities })=> { return ( <Card .... > <Meta description={<ul> {abilities.map(ability => <li key={ability.ability.name} >{ability.ability.name}</li>)} </ul>} /> </Card> ) }
este fue el resultado:
Mi aporte con el reto de las habilidades y también agregue los tipos. Ademas agregue una etiqueta de color por cada tipo de pokemon. .
import React from 'react' import { Card, Badge } from 'flowbite-react'; import { FiHeart } from "react-icons/fi"; function PokemonCard(props) { const typeToColorClass = { bug: "bg-lime-100 text-lime-900 dark:bg-lime-900 dark:text-lime-300", dark: "bg-gray-200 text-white dark:bg-gray-400 dark:text-gray-900", dragon: "bg-indigo-200 text-indigo-900 dark:bg-indigo-700 dark:text-indigo-200", electric: "bg-yellow-200 text-black dark:bg-yellow-600 dark:text-yellow-100", fairy: "bg-pink-200 text-pink-900 dark:bg-pink-800 dark:text-pink-100", fighting: "bg-red-400 text-white dark:bg-red-600 dark:text-red-100", fire: "bg-red-200 text-red-800 dark:bg-red-900 dark:text-red-100", flying: "bg-teal-100 text-teal-700 dark:bg-teal-700 dark:text-teal-100", ghost: "bg-purple-300 text-white dark:bg-purple-700 dark:text-purple-200", grass: "bg-green-100 text-green-800 dark:bg-green-700 dark:text-green-200", ground: "bg-amber-900 text-white dark:bg-amber-600 dark:text-brown-100", ice: "bg-cyan-100 text-cyan-900 dark:bg-lightblue-700 dark:text-lightblue-100", normal: "bg-gray-300 text-black dark:bg-gray-500 dark:text-gray-100", poison: "bg-purple-200 text-purple-900 dark:bg-purple-700 dark:text-purple-200", psychic: "bg-pink-200 text-pink-900 dark:bg-pink-600 dark:text-pink-100", rock: "bg-slate-300 text-slate-900 dark:bg-amber-500 dark:text-brown-100", steel: "bg-gray-400 text-black dark:bg-gray-600 dark:text-gray-200", water: "bg-blue-200 text-blue-900 dark:bg-blue-800 dark:text-blue-100" }; return ( <Card className='' imgAlt={props.name} imgSrc={props.image} > <h1 className="text-2xl font-bold tracking-tight text-gray-900 dark:text-white"> {`${props.index} - ${props.name}`} </h2> <Badge className="bg-red-100 text-red-800 text-xs font-bold mr-2 px-2.5 py-0.5 rounded dark:bg-red-900 dark:text-red-300"> ID: {props.id} </Badge> <h3>Type: </h3> <p> {props.types.map(object => { const colorClass = typeToColorClass[object.type.name] || typeToColorClass.default; return ( <span key={object.type.name} className={`text-md font-bold mr-2 px-3.5 py-2 rounded ${colorClass}`} > {object.type.name} </span> ) })} </p> <h3 className='font-bold'>Abilities: </h4> <p className="font-normal text-gray-700 dark:text-gray-400"> {props.abilities.map(object => ( <li key={object.ability.name} > {object.ability.name} </li> ) )} </p> <button> <FiHeart/> </button> </Card> ) } export { PokemonCard }
PokemonList.jsx
import React from 'react' import { PokemonCard } from '../PokemonCard' import { v4 as uuidv4 } from 'uuid'; function PokemonList({ pokemons }) { // console.log('file: PokemonList.jsx | Line 8: ',pokemons) return ( <div className='PokemonList grid grid-cols-4 gap-4 flex-col w-full m-10 '> {pokemons.map((pokemon, index) => { const id = uuidv4(); return <PokemonCard key={index} id={id} index={index} name={pokemon.name} image={pokemon.sprites.front_default} abilities={pokemon.abilities} types={pokemon.types} /> })} </div> ) } PokemonList.defaultProps = { pokemons : Array(10).fill('') // [''],[''],[''], ...x10 } export { PokemonList }
. API
import axios from 'axios' const API_URL = 'https://pokeapi.co/api/v2/pokemon?limit=151' export const getPokemon = async () => { return axios .get(API_URL) .then((res) => res.data.results) .catch((err) => console.log(err)) } export const getPokemonDetails = (pokemon) => { return axios.get(pokemon.url) .then(res => res.data) .catch((err) => console.log(err)) }
Hay algo realmente molesto con este curso, al menos a mí me lo parece; el texto está muy chiquito, te toca poner pantalla completa para poder ver :c
LO LOGREEEEEEEE
Yo saqué los nombres de habilidades desde el componente PokelmonList
export default function PokemonList() { const pokemons = useSelector(state => state.pokemons) return ( <div className="PokemonList"> {pokemons.map((pokemon) => { let abilities = pokemon.abilities.map(ability => ability.ability.name).join(', ') return <PokemonCard name={pokemon.name} key={pokemon.name} img={pokemon.sprites.front_default} abilities={abilities} /> })} </div> ) }
pdta: si, ability.ability es absurdo, creo que muchas veces uno pierde mucho tiempo por no ver bien cómo vienen las propiedades de los objetos, que a veces son absurdas pero pues es lo que hay :'v
Ahí hago uso del useSelector que sería acceder al store del redux, porque sino estamos haciendo uso de los props entonces daría igual usar redux (para mi), no entiendo por qué la profe no lo hace :p
Esta clase estuvo algo desafiante:
Dejo mi código para otros viajeros que se atrevieron a seguir esta senda: Estoy dejando todo el codigo que tuve que modificar , por eso el numero de archivos.
import { FC } from "react"; import { PokemonDetailType } from "../api"; import { PokemonCard } from "./PokemonCard"; import './PokemonList.css' interface Props { pokemons: PokemonDetailType[] } const PokemonList:FC<Props> = ({ pokemons }) =>{ return ( <div className="PokemonList"> {pokemons.map((pokemon,index)=>( <PokemonCard name={pokemon.name} types={pokemon.types} image={pokemon.sprites.other.home.front_default} key={pokemon.name} /> ))} </div> ) } PokemonList.defaultProps = { pokemons: Array(10).fill('') } export { PokemonList }
import { FC } from "react"; import { StarOutlined } from "@ant-design/icons"; import { Card } from "antd"; import Meta from "antd/lib/card/Meta"; type Props = { name: string, image: string, types: any[] } const PokemonCard:FC<Props> = ({name, image, types}) => { return ( <Card title={name} cover={<img src={image} alt={name} />} extra={<StarOutlined />} > <Meta description={types.map(type=>type.type.name).join()} /> </Card> ) } export { PokemonCard }
import { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' import { Search } from './components/Search' import { PokemonList } from './components/PokemonList' import { Col } from 'antd' import { getPokemonDetails, getPokemons, PokemonType } from './api' import { setPokemons } from './actions' import { TypeState } from './reducers/pokemon' import logo from './statics/logo.svg' import './App.css' function App() { const pokemons = useSelector((state:TypeState)=>state.pokemons) const dispath = useDispatch(); useEffect(()=>{ async function fetchPokemon(){ const pkmns = await getPokemons(); const pkmnsDetailed = await Promise.all((pkmns as PokemonType[]).map(pkmn=>getPokemonDetails(pkmn))) dispath(setPokemons(pkmnsDetailed)) } fetchPokemon(); },[]) return ( <div className="App"> <Col span={4} offset={10}> <img src={logo} alt="Pokedux" /> </Col> <Col span={8} offset={8}> <Search /> </Col> <PokemonList pokemons={pokemons} /> </div> ) } export default App;
import { PokemonDetailType, PokemonType } from "../api"; import { SET_POKEMONS } from "./types"; export const setPokemons = (payload: PokemonDetailType[])=>({ type: SET_POKEMONS, payload })
import { PokemonDetailType, PokemonType } from "../api"; import { SET_POKEMONS } from "./types"; export const setPokemons = (payload: PokemonDetailType[])=>({ type: SET_POKEMONS, payload })
import { SET_POKEMONS } from "../actions/types" import { PokemonDetailType } from "../api" export type TypeState = { pokemons: PokemonDetailType[] } const initialState: TypeState = { pokemons: [] } export const pokemonReducer = (state:TypeState = initialState, action:any)=>{ switch(action.type){ case SET_POKEMONS: return {...state, pokemons: action.payload} default: return state } }
Genial, pero el desafio era pasar las habilidades, no los tipos jajaja un saludo!
Mi seguimiento con TypeScript. He tenido que cambiar varias cosas dentro del código, pero quedó mucho mejor y entendible para todos. Recibo sus comentarios y espero que ayude a quién lo pueda necesitar.
.
// index.tsx import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import { pokemonsReducer } from './reducers/pokemons'; import { Provider } from 'react-redux'; import { applyMiddleware, compose, legacy_createStore as createStore } from 'redux'; import { logger } from './middlewares'; import './index.css'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); const composedEnhancers = compose( applyMiddleware(logger), (window as any).__REDUX_DEVTOOLS_EXTENSION__ && (window as any).__REDUX_DEVTOOLS_EXTENSION__(), ) const store = createStore(pokemonsReducer, composedEnhancers); root.render( <Provider store={store}> <React.StrictMode> <App /> </React.StrictMode> </Provider> );
.
// App.tsx import React, { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { Col } from "antd"; import Searcher from "./components/Searcher"; import PokemonList from "./components/PokemonList"; import { getPokemon, getPokemonDetails } from "./api"; import { setPokemonsDetails } from "./actions"; import { IPokemonDetails } from "./types"; import logo from "./statics/logo.svg"; import "./App.css"; type AppType = { pokemons: IPokemonDetails[]; }; function App() { const pokemons = useSelector((state: AppType) => state.pokemons); const dispatch = useDispatch(); useEffect(() => { const fetchPokemons = async () => { const response = await getPokemon(); if (response) { const pokemonsDetails = await Promise.all( response.map((pokemon) => getPokemonDetails(pokemon)) ); dispatch(setPokemonsDetails(pokemonsDetails)); } }; fetchPokemons(); }, [dispatch]); return ( <div className="App"> <Col span={4} offset={10}> <img src={logo} alt="Pokedux" /> </Col> <Col span={8} offset={8}> <Searcher /> </Col> <PokemonList pokemons={pokemons} /> </div> ); } export default App;
.
// types/index.ts export interface IPokemonDetails { abilities: []; // base_experience: number; // forms: []; // game_indices: []; // height: number; // held_items: any[]; // id: number; // is_default: boolean; // location_area_encounters: string; // moves: []; name: string; // order: number; // past_types: any[]; // species: []; sprites: SpritesType; // stats: []; // types: []; // weight: number; } export interface IPokemonType { name: string; url: string; } export type InitialPokemonsStateType = { pokemons: IPokemonType[]; }; export type SpritesType = { back_default: string; back_female: string | null; back_shiny: string; back_shiny_female: string | null; front_default: string; front_female: string | null; front_shiny: string; front_shiny_female: string | null; };
.
// api/responses/index.ts import { IPokemonType } from "../../types"; export type PokemonResponseType = { results: IPokemonType[]; }; // api/index.ts import axios from "axios"; import { IPokemonDetails } from "../types"; import { PokemonResponseType } from "./responses"; export const getPokemon = async () => { const URL = "https://pokeapi.co/api/v2/pokemon?limit=151"; try { const { data } = await axios.get<PokemonResponseType>(URL); const { results } = data; return results; } catch (err) { console.error("err ".repeat(5), err); } }; export const getPokemonDetails = async (pokemon: any) => { try { const { data } = await axios.get<IPokemonDetails>(pokemon.url); return data; } catch (err) { console.error("err ".repeat(5), err); } };
.
// reducers/pokemons.ts import { SET_POKEMONS_DETAILS } from "../actions/types"; import { InitialPokemonsStateType } from "../types"; const initialState: InitialPokemonsStateType = { pokemons: [], }; export const pokemonsReducer = ( state: InitialPokemonsStateType = initialState, action: any ) => { switch (action.type) { case SET_POKEMONS_DETAILS: return { ...state, pokemons: action.payload, }; default: return state; } };
.
// components/PokemonList import React from "react"; import { IPokemonDetails } from "../types"; import PokemonCard from "./PokemonCard"; import "./PokemonList.css"; type PokemonListProps = { pokemons: (IPokemonDetails | undefined)[]; }; const PokemonList = (props: PokemonListProps) => { return ( <div className="PokemonList"> {props.pokemons.map( (pokemon) => pokemon && <PokemonCard key={pokemon.name} pokemon={pokemon} /> )} </div> ); }; export default PokemonList; // components/pokemonCard.tsx import React from "react"; import { Card } from "antd"; import Meta from "antd/lib/card/Meta"; import { StarOutlined } from "@ant-design/icons"; import { IPokemonDetails } from "../types"; type PokemonCardProps = { pokemon: IPokemonDetails; }; const PokemonCard = (props: PokemonCardProps) => { return ( <Card style={{ width: 250 }} title={props.pokemon.name} cover={<img src={props.pokemon.sprites.front_default} alt={props.pokemon.name} />} extra={<StarOutlined />} > <Meta description="fire, magic" /> </Card> ); }; export default PokemonCard;
Adjunto el ejercicio incluidas las habilidades de cada pokemon, como buena practica, es recomendable separar la lógica (reduce, maps, condicionales) de las vistas jsx en archivos separados comúnmente llamados customHooks
Al 15 de Julio de 2026 el link
Esta roto
Lo preocupante es que cada vez hay más de estos en los cursos, ¿será que la plataforma esta quedando resagada?
Como opinión del ejercicio, tal vez valdría la pena tener cuidado en cuál es la información que guardamos en la store y la que llamamos de la API pues me parece que de cada uno de los endpoints llamamos 2 estructuras de datos distintas pero la metemos en la misma store:
// Estructura de datos de API 1 export interface PokemonDataType { name: string; url?: string; } // Estructura de datos de API 2 export interface PokeType { type: { name: string; }; } export interface PokeDetail { id: number; name: string; sprites: { front_default: string; }; types: PokeType[]; // Ambas estructuras las guardamos en: const pokemons = useSelector( (state: RootState) => state.pokemonData.pokemons, ); const dispatch = useDispatch();
Les comparto como quedaría en TypeScript:
// app/types/pokeTypes.ts export interface PokemonDataType { name: string; url?: string; type?: string; } export interface PokeType { type: { name: string; }; } export interface PokeDetail { id: number; name: string; sprites: { front_default: string; }; types: PokeType[]; } export interface PokemonResponseType { count: number; next: string | null; previous: string | null; results: PokeDetail[]; } // app/actions/index.ts import { SET_POKEMONS } from '~/types/actionTypes'; import type { PokeDetail } from '~/types/pokeTypes'; export const setPokemons = (payload: PokeDetail[]) => ({ type: SET_POKEMONS, payload, }); // app/welcome/welcome.tsx import { useEffect } from 'react'; import { Col } from 'antd'; import { useDispatch, useSelector } from 'react-redux'; import logo from '../statics/logo.svg'; import { PokeList } from '~/components/PokeList'; import { Searcher } from '~/components/Searcher'; import { getPokemon } from '~/api/getPokemon'; import { getPokemonDetails } from '~/api/getPokemonDetails'; import { type RootState } from '~/store/store'; import { setPokemons } from '~/actions'; import type { PokemonDataType, PokeDetail } from '~/types/pokeTypes'; export function Welcome() { const pokemons = useSelector( (state: RootState) => state.pokemonData.pokemons, ); const dispatch = useDispatch(); useEffect(() => { const fetchPokemons = async () => { const pokeRes = await getPokemon(); if (pokeRes) { const pokeDetailsRaw = await Promise.all( pokeRes.map((poke: PokemonDataType) => getPokemonDetails(poke)), ); const pokeDetails = pokeDetailsRaw.filter( (poke): poke is PokeDetail => poke !== undefined, ); dispatch(setPokemons(pokeDetails)); } }; fetchPokemons(); }, [getPokemon, setPokemons, getPokemonDetails]); return ( <main> <h1>Pokedux</h1> <Col span={4} offset={10} className='my-3'> <img src={logo} alt='Pokedux' /> </Col> <Col span={8} offset={8} className='mt-3'> <Searcher /> </Col> {pokemons && <PokeList pokemons={pokemons} />} </main> ); } // app/components/PokeList.tsx import { PokeCard } from './PokeCard'; import { type PokeDetail } from '~/types/pokeTypes'; export const PokeList = ({ pokemons }: { pokemons: PokeDetail[] }) => { return ( <> <div className='row w-100 justify-content-center '> {pokemons.map((pokemon) => ( <PokeCard key={pokemon.name} name={pokemon.name} type={pokemon.types[0].type.name} imgUrl={pokemon.sprites.front_default} className='col-sm-3 m-3 ' /> ))} </div> </> ); }; // app/components/PokeCard.tsx import { StarOutlined } from '@ant-design/icons'; import { Card } from 'antd'; import { Meta } from 'antd/es/list/Item'; interface PokeCardProps { name: string; className?: string; type: string; imgUrl: string; } export const PokeCard = ({ name, type, imgUrl, className }: PokeCardProps) => { return ( <Card title={name} className={className} extra={<StarOutlined />} cover={<img src={imgUrl} alt={name} className='w-100' />}> <Meta description={type} /> </Card> ); };
Mi solucion
const PokemonCard = ( { name, image, description }) => { return <Card title={name} cover={<img src={image} alt={name} />} extra={<StarOutlined />} > <Meta description={description.map(item => item.type.name).join(", ")} /> </ Card> }
🖼️ Consumo de Imágenes y Datos Detallados (PokeAPI)
Para mostrar imágenes, no basta con el primer llamado a la API. Se requiere un segundo nivel de consulta para obtener los detalles de cada Pokémon (sprites, habilidades, tipos).
🔄 El Flujo de Datos Asíncrono
Promise.all para esperar a que todas las peticiones terminen antes de actualizar el estado.🛠️ Implementación Técnica
1. Función de Detalle (API Layer)
Extraemos la data específica de la URL individual de cada Pokémon.
const getPokemonDetails = (pokemon) => { return axios.get(pokemon.url) .then(res => res.data) .catch(err => console.error(err)); };
¡Qué gran avance, Juan Diego! Pasar de una lista de nombres a una galería visual cambia totalmente la experiencia del usuario (UX). Manejar múltiples peticiones asíncronas con Promise.all es una de las habilidades más valoradas en el desarrollo frontend porque optimiza el rendimiento drásticamente.
2. Lógica en el Componente (App.js)
Aquí es donde ocurre la magia de Promise.all:
JavaScript
const fetchPokemons = async () => { const pokemonsRes = await getPokemons(); // Lista básica // Creamos un array de promesas de detalles const pokemonsDetailed = await Promise.all( pokemonsRes.map(pokemon => getPokemonDetails(pokemon)) ); // Guardamos en Redux la data completa (incluyendo sprites) dispatch(setPokemons(pokemonsDetailed)); };
🎨 Actualización de Componentes UI
Para que las imágenes se vean, debemos navegar por el objeto de respuesta hasta llegar a la propiedad sprites.
JavaScript
// En el mapeo de la lista: <PokemonCard name={pokemon.name} image={pokemon.sprites.front_default} // 👈 Path clave />
💡 Datos Pro para tus Notas
Promise.all es "todo o nada". Si una petición falla, todas fallan. En aplicaciones críticas, podrías investigar Promise.allSettled.sprites de la PokeAPI es gigante. front_default es la imagen clásica, pero también tienes front_shiny o versiones de alta resolución en other.official-artwork.alt={name} en el tag <img> por accesibilidad y SEO.🚀 Reto: Habilidades (Abilities)
Para cumplir el desafío de la clase: En el componente PokemonCard, puedes mapear pokemon.abilities para mostrar los nombres de los poderes debajo de la imagen. ¡Inténtalo!
Vamos a reemplazar las imágenes por defecto que tenemos de los Pokémon 🐣 por sus sprites oficiales 🎨.
Cuando hacemos el fetch a la API, en la información de cada Pokémon encontramos una propiedad llamada url, que tiene un formato como este:
Si hacemos fetch a esa URL, dentro de la respuesta encontraremos el sprite del Pokémon en la propiedad:
sprites.front_default, cuya ruta luce así:
💡 Analizando el patrón
Notemos que el único elemento que cambia en esa URL es el número entre /pokemon/ y .png.
Por ejemplo:
/pokemon/25.png ⚡/pokemon/1.png 🌱Entonces, en lugar de hacer un fetch adicional por cada Pokémon, podemos extraer el ID directamente desde la URL que ya tenemos y usarlo para construir el enlace del sprite oficial 🚀.
🧠 Extrayendo el ID con Regex
Para obtener ese ID, usamos un pequeño truco con expresiones regulares (regex):
const id = url?.match(/\/pokemon\/(\d+)\/?$/)?.[1];
👉 Aquí:
/pokemon/ busca esa parte literal de la URL.(\d+) captura el número del Pokémon.$ indica el final de la cadena.[1] obtiene el grupo capturado (el número).Con ese id, construimos la URL del sprite así:
const spriteUrl = `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${id}.png`;
⚙️ Código final
PokemonList.jsx
import PokemonCard from "./PokemonCard"; import "./PokemonList.css"; const PokemonList = ({ pokemons = Array(10).fill("") }) => { return ( <div className="PokemonList"> {pokemons.map((pokemon, index) => ( <PokemonCard name={pokemon.name} url={pokemon.url} key={index} /> ))} </div> ); }; export default PokemonList;
PokemonCard.jsx
import { Card } from "antd"; import Meta from "antd/es/card/Meta"; const PokemonCard = ({ name, url }) => { const id = url?.match(/\/pokemon\/(\d+)\/?$/)?.[1]; const spriteUrl = id ? `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${id}.png` : ""; return ( <Card title={name} cover={<img src={spriteUrl} alt={name} />} > <Meta description="fire, magic" /> </Card> ); }; export default PokemonCard;
🏁 Resultado
✅ Reemplazamos las imágenes por defecto con los sprites oficiales sin necesidad de hacer múltiples fetch adicionales.
💨 Esto hace el proceso más rápido, eficiente y limpio.
🎯 Además, aprendimos a usar regex para extraer datos útiles de una URL.
Alo! 👋 Dejo versión del branch y commit de la clase para practicar el inglés:
- English version branch& commit:
Branch name:
feat/pokemon-images
Commit message:
feat: display Pokemon images in UI
- Implement image request for each Pokemon.
- Add getPokemonDetails function in src/api/index.js to fetch detailed Pokemon data including images.
- Update src/App.js to use getPokemonDetails and fetch images in useEffect.
- Update Pokemon components to display images (if applicable, based on previous UI).
No se si sea la mejor forma pero se ve bien en la UI.