A Simple Pokedex App in React
Jeff Wilkey
Posted on September 3, 2019
The Goal
Have a searchable list of the original 150 pokemon, when a list item is clicked display the associated card and readable information about it.
Execution
I decided to build the project on React since it's fast and being able to separate different elements into components will help significantly. Then as far as the layout goes I went with a very simple UI (no CSS framework) and utilized the React-Select package for a searchable dropdown:
I also added a flat logo I found on google images, (couldn't find the author but, kudos!) and added a little bounce animation. While it's unimportant to the functionality of the site I really like adding little quirks like that. Heres a pen demonstrating the bounce animation (infinitely):
Utilizing an API
I was initially going to webscrape the original 150 pokemon names but luckily I found this Gist by octalmage which turned out to be exactly what I needed for React-Select. And with a simple map:
// ./utils/pokemonOptions.js
const Pokemon=new Array("Bulbasaur","Ivysaur","Venusaur","Charmander","Charmeleon","Charizard","Squirtle","Wartortle","Blastoise","Caterpie","Metapod","Butterfree","Weedle","Kakuna","Beedrill","Pidgey","Pidgeotto","Pidgeot","Rattata","Raticate","Spearow","Fearow","Ekans","Arbok","Pikachu","Raichu","Sandshrew","Sandslash","Nidoran","Nidorina","Nidoqueen","Nidoran","Nidorino","Nidoking","Clefairy","Clefable","Vulpix","Ninetales","Jigglypuff","Wigglytuff","Zubat","Golbat","Oddish","Gloom","Vileplume","Paras","Parasect","Venonat","Venomoth","Diglett","Dugtrio","Meowth","Persian","Psyduck","Golduck","Mankey","Primeape","Growlithe","Arcanine","Poliwag","Poliwhirl","Poliwrath","Abra","Kadabra","Alakazam","Machop","Machoke","Machamp","Bellsprout","Weepinbell","Victreebel","Tentacool","Tentacruel","Geodude","Graveler","Golem","Ponyta","Rapidash","Slowpoke","Slowbro","Magnemite","Magneton","Farfetch'd","Doduo","Dodrio","Seel","Dewgong","Grimer","Muk","Shellder","Cloyster","Gastly","Haunter","Gengar","Onix","Drowzee","Hypno","Krabby","Kingler","Voltorb","Electrode","Exeggcute","Exeggutor","Cubone","Marowak","Hitmonlee","Hitmonchan","Lickitung","Koffing","Weezing","Rhyhorn","Rhydon","Chansey","Tangela","Kangaskhan","Horsea","Seadra","Goldeen","Seaking","Staryu","Starmie","Mr. Mime","Scyther","Jynx","Electabuzz","Magmar","Pinsir","Tauros","Magikarp","Gyarados","Lapras","Ditto","Eevee","Vaporeon","Jolteon","Flareon","Porygon","Omanyte","Omastar","Kabuto","Kabutops","Aerodactyl","Snorlax","Articuno","Zapdos","Moltres","Dratini","Dragonair","Dragonite","Mewtwo","Mew");
export default Pokemon.map(p => ({
value: p.toLowerCase(),
label: p
}));
I was able to add a dynamic searchable select field with 150 Pokemon:
Next, I did some research on different Pokemon related API's. (There's a few out there). Eventually, pokemontcg.io made itself apparent as the best option for what we'll need.
After some tinkering around with the pokemontcg SDK, I figured out that I wanted cards from the "Base"
series and needed to filter results returned from the API to make sure I receive the exact card I'm looking for.
Utilization of the pokemontcg SDK looks something like this:
// pass handleChange in as the onChange prop for the React-Select field
handleChange = e => {
if (this.state.selected !== e.value) {
pokemon.card.where({ name: e.value, series: "Base" }).then(cards => {
// filter out cards that dont match name exactly
cards = cards.filter(card => card.name === e.label);
this.setState({
selected: e.value,
index: 0,
cards
});
});
}
};
Then finally with this call we receive a few cards and select the one at index 0. Which up to this point has been completely accurate but is definitely not foolproof. However, it's good enough for this application and we use this data (shown below) to display information on the page:
"card": {
"id": "base6-3",
"name": "Charizard",
"nationalPokedexNumber": 6,
"imageUrl": "https://images.pokemontcg.io/base6/3.png",
"imageUrlHiRes": "https://images.pokemontcg.io/base6/3_hires.png",
"types": [
"Fire"
],
"supertype": "Pokémon",
"subtype": "Stage 2",
"evolvesFrom": "Charmeleon",
"ability": {
"name": "Energy Burn",
"text": "As often as you like during your turn (before your attack), you may turn all Energy attached to Charizard into R for the rest of the turn. This power can't be used if Charizard is Asleep, Confused, or Paralyzed.",
"type": "Poké-Power"
},
"hp": "120",
"retreatCost": [
"Colorless",
"Colorless",
"Colorless"
],
"convertedRetreatCost": 3,
"number": "3",
"artist": "Mitsuhiro Arita",
"rarity": "Rare",
"series": "Base",
"set": "Legendary Collection",
"setCode": "base6",
"attacks": [
{
"cost": [
"Fire",
"Fire",
"Fire",
"Fire"
],
"name": "Fire Spin",
"text": "Discard 2 Energy cards attached to Charizard in order to use this attack.",
"damage": "100",
"convertedEnergyCost": 4
}
],
"resistances": [
{
"type": "Fighting",
"value": "-30"
}
],
"weaknesses": [
{
"type": "Water",
"value": "×2"
}
]
}
Then after some styling, we end up with the following product shown in the image and codesandbox. I've also hosted it on heroku here.
UPDATE Jan. 2021: If no one has visited the site recently it'll take about 30 seconds to spin up and may require a refresh if you hit the heroku error screen. I'll try to fix this as well as update codesandbox when I have time. It's really worth checking it out on Heroku though because I've added support for hundreds more pokemon as well as the ability to flip through cards from different series for each one.
Thank you very much for reading, I hope this article helps or inspires someone. I will be continuing to contribute to this project via this GitHub Repo feel free to star if you want to keep up with progress or contact me if you'd like to contribute.
Things to add or be worked on
- A lot of refactoring
Support for all PokemonShow all versions of cards
Posted on September 3, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.