loading...
Cover image for A Simple Pokedex App in React

A Simple Pokedex App in React

jeffwilkey profile image Jeff Wilkey Updated on ・4 min read

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:

Paper Layout

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:
React Select Field

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 figure below which is hosted on Heroku here:
Final App

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 Pokemon
  • Show all versions of cards
  • ..probably a lot more :)

Discussion

pic
Editor guide