Building a Pokedex with React

Published Nov 16, 2016Last updated Jan 18, 2017
Building a Pokedex with React

What if Pokémon were real? As a kid growing up in the 90s, we've all wondered about this; catching Pokémon, battling in gyms, and using that cool Pokédex to give us the information we need. Obviously, there are no Pokémon in real life (Pokémon Go is the closest thing we can get to "real") but that doesn't mean we can't have a cool Pokédex, right?

So let's build our own Pokédex using React!

(Read related article: What if Programming Languages were Pokémon?)

React.js Library

Facebook has come up with the React library which has shaken the front-end world with its shrewdness. Its rendering capabilities are quite fantastic and the simplistic JSX syntax makes things even better. Let's check out how we can build a really cool Pokédex with React and a few npm tools.

Before we go any further, please make sure that you have Node.js and npm installed on your machine. If you're a beginner, here's a guide to help you be familiar with the React environment.

Let's get started!

1. Installing dependencies

Create a folder for your project and open the terminal in the root of the folder. To initialize your project, type:

npm init

You might need to press the Enter key a few times to set up your package.json

Now run:

npm install --save-dev babel-cli babel-loader babel-plugin-add-module-exports babel-plugin-transform-runtime babel-preset-es2015 babel-preset-react babel-preset-stage-2 isomorphic-fetch react react-dom react-hot-loader webpack webpack-dev-server

This will install React into your project and also a few Babel plugins and presets that will come handy in transpiling (converting) our ES6 + React code into ES5 JavaScript so that browsers are able to run it.
Remember, not all browsers are able to run ES6 yet.

2. Setting up Pokémon monster sprites

  1. Create a folder named public in the root of your project.
  2. Download this zip file from here.
  3. Extract this zip file into your public folder.
  4. Make sure that your folder structure so far looks like this.

pokedex

3. Webpack & Babel configuration

Webpack is a tool that allows us to bundle all of our JavaScript files into ES5 using Babel plugins. So, all we do is run our code in React and ES6 and Webpack helps us convert it instantly into ES5 standards. Let's add a webpack.config.js file to our project.

var path = require('path');
var webpack = require('webpack');
var assetsPath = path.join(__dirname, 'src');

module.exports = {
    entry :  {
        bundle :  ['webpack-dev-server/client?http://0.0.0.0:8080', 
            'webpack/hot/only-dev-server',
           path.resolve(assetsPath,'index.js')],
    },
    output: {
        chunkFilename: '[name].js',
        filename: '[name].js', //
        path: path.join(assetsPath ,"dist/js/"),
        publicPath: 'http://localhost:8080/assets/'
    },
    module: {
        loaders: [
            {
                //tell webpack to use jsx-loader for all *.jsx files
                test: /.jsx?$/,
                loaders: ['react-hot-loader/webpack','babel'],
                include: [path.resolve(assetsPath)]
           }
        ]
    },
    resolve: {
        extensions: ['', '.js', '.jsx']
    },
    devtool : '#source-map',

    plugins: [
     new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': '"development"'
      }
    }),
    new webpack.HotModuleReplacementPlugin()
    ]
};

3.1 Understanding the Webpack configuration

  1. entry – This is where Webpack looks for as a starting point for the bundle. index.js within src folder of our project is where the Pokedex React app resides in. We will create this shortly.
  2. output – The publicPath key is crucial in this configuration. This means that our JavaScript will be served at http://localhost:8080/assets/bundle.js.
  3. loaders – This tells Webpack how it should render files with a particular extension. In our case, all files with .js or .jsx extension are treated as files that need to be converted to ES5 using Babel and React loaders.

You can also read more about Webpack in this getting started tutorial.

Create a .babelrc file in the root folder of the project and have this as its content.

{
  "presets" : ["es2015", "stage-2", "react"],
  "plugins" : ["add-module-exports","transform-runtime"]
}

This file tells Babel that when Webpack loads the babel-loader, it should transpile it to ES6 and React using these presets and plugins. You can read more about them here.

4. Index.html and style.css

Create an index.html file in the root folder of the project, which should look like this:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Pokedex | React</title>
    <link rel="stylesheet" href="style.css" media="screen" title="no title">
  </head>
  <body>
    <div id="app">

    </div>
    <script src="http://localhost:8080/assets/bundle.js" charset="utf-8"></script>
  </body>
</html>

Let's add a few styles to make our app look nicer. Create a style.css file in the root folder of the project with this CSS.

HTML, body{
  font-family : sans-serif;
  background: #e2e1e0;
    text-align : center;
}
.pokeapp{
    padding : 16px;
}
.pokemon--species--list{
    display : -webkit-box;
    display : -ms-flexbox;
    display : flex;
    -ms-flex-wrap : wrap;
        flex-wrap : wrap;
    padding : 16px 0;
}
.pokemon--species{
    -ms-flex-preferred-size : 25%;
        flex-basis : 25%;
    padding : 8px;
    box-sizing: border-box;

}
.pokemon--species--container{
    padding : 8px;
    background : white;
    cursor : pointer;
 box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
}

5. Scaffolding a React component

Create a folder named src, if you haven't already done that. Create a file index.js within the src folder.

Lets us first create a React Component named PokeApp. This component will be the root component of this project.
Also, in our index.html, we created a div with id app remember? Let's mount our PokeApp onto the div#app.

//This is the root component
import {render} from 'react-dom';
import React, {Component} from 'react';

class PokeApp extends Component{
  render(){
    return <div className="pokeapp">
      <h1> The Kanto PokeDex! </h1>
    </div>;
  }
}
render(<PokeApp/>,document.getElementById('app'))

Right now, our app doesn't do anything but let's go ahead and run it with Webpack and see what we have so far. All we have to do is to run:

node_modules/webpack-dev-server/bin/webpack-dev-server.js

in the terminal in the root folder of the project. Also, open up http://localhost:8080. You should see this on your screen.

Preview Initial

6. Fetching Pokedex from the PokeApi API

Paul Hallett has been very kind to build a killer of an API to help developers build apps on Pokémon. You can read more about it here.

Let's enhance our index.js and add a couple of components. We will create a component named Pokemon to display an individual Pokémon and we will create another component named PokemonListto fetch Pokédex data and display a list of Pokemon components.

5.1 Pokémon Component

Pokemon component should be a rather simple component which accepts two props, id as well as pokemon. id is the ID of the Pokémon on the Pokédex. For eg: Charmander has an id - 4. And Charmander's pokemon object would look like this.

{
url: "http://pokeapi.co/api/v2/pokemon/4/",
name: "charmander"
}

So, let's go ahead and create it.

//The Pokemon component will show an individual Pokemon monster
// It shows an image of the Pokemon and
// shows the name of it as well.
class Pokemon extends Component{
  render(){
    const {pokemon,id} = this.props;
    return <div className="pokemon--species">
            <div className="pokemon--species--container">
              <div className="pokemon--species--sprite">
                <img src={`/public/sprites/${id}.png`} />
              </div>
              <div className="pokemon--species--name"> {pokemon.name} </div>
            </div>
          </div>;
    }
}

5.2 PokemonList Component

The PokemonList component is responsible for fetching the Pokemon data from the PokeApi. It should look something like this.

//The PokemonList component shows nothing when it mounts for the first time. 
//But right before it mounts on to the DOM, it makes an 
//API call to fetch the first 151 Pokemon from the API and 
//then displays them using the Pokemon Component

class PokemonList extends Component{
  constructor(props){
    super(props);
    this.state = {
      species : [],
      fetched : false,
      loading : false,
    };
  }
  componentWillMount(){
    this.setState({
      loading : true
    });
    fetch('http://pokeapi.co/api/v2/pokemon?limit=151').then(res=>res.json())
    .then(response=>{
      this.setState({
        species : response.results,
        loading : true,
        fetched : true
      });
    });
  }

  render(){
    const {fetched, loading, species} = this.state;
    let content ;
    if(fetched){
      content = <div className="pokemon--species--list">{species.map((pokemon,index)=><Pokemon key={pokemon.name} id={index+1} pokemon={pokemon}/>)}</div>;
    }else if(loading && !fetched){
        content = <p> Loading ...</p>;
    }
    else{
      content = <div/>;
    }
    return  <div>
      {content}
    </div>;
  }
}

It starts off with a state wherein the species array is empty and the fetched boolean is set to false. This tells the component that when it is in this state, it does not have much to show to the user.

When the component is just about to mount, however, the loading boolean is set to true which allows us to show a nice Loading ... text on our screen while the isomorphic-fetch library fetches the Pokedex data from the API, converts it to JSON and loads it into the species array. At this point loading is set to false and fetched is set to true. This means that the app now has all the Pokémon loaded in its state.

Now, all we need to do is iterate over all the Pokemon in the species array and for each of the Pokemon render the Pokemon component with id(since the index is 0-based, id = index + 1) and pokemon props set accordingly.

And, that's it!

Now that we have our components ready. Let's go ahead and add them to our PokeApp Component. So by now, our index.js file should look like this.

import {render} from 'react-dom';
import React, {Component} from 'react';
import fetch from 'isomorphic-fetch';


//The Pokemon component will show an individual Pokemon monster
// It shows an image of the Pokemon and
// shows the name of it as well.
class Pokemon extends Component{
  render(){
    const {pokemon,id} = this.props;
    return <div className="pokemon--species">
            <div className="pokemon--species--container">
              <div className="pokemon--species--sprite">
                <img src={`/public/sprites/${id}.png`} />
              </div>
              <div className="pokemon--species--name"> {pokemon.name} </div>
            </div>
          </div>;
    }
}


//The PokemonList component shows nothing when it mounts for the first time. 
//But right before it mounts on to the DOM, it makes an 
//API call to fetch the first 151 Pokemon from the API and 
//then displays them using the Pokemon Component

class PokemonList extends Component{
  constructor(props){
    super(props);
    this.state = {
      species : [],
      fetched : false,
      loading : false,
    };
  }
  componentWillMount(){
    this.setState({
      loading : true
    });
    fetch('http://pokeapi.co/api/v2/pokemon?limit=151').then(res=>res.json())
    .then(response=>{
      this.setState({
        species : response.results,
        loading : true,
        fetched : true
      });
    });
  }

  render(){
    const {fetched, loading, species} = this.state;
    let content ;
    if(fetched){
      content = <div className="pokemon--species--list">{species.map((pokemon,index)=><Pokemon key={pokemon.name} id={index+1} pokemon={pokemon}/>)}</div>;
    }else if(loading && !fetched){
        content = <p> Loading ...</p>;
    }
    else{
      content = <div/>;
    }
    return  <div>
      {content}
    </div>;
  }
}


//This is the root component
class PokeApp extends Component{
  render(){
    return <div className="pokeapp">
      <h1> The Kanto PokeDex! </h1>
      <PokemonList/>
    </div>;
  }
}

render(<PokeApp/>,document.getElementById('app'))

Let's have a look at our app now. BAM!!

Pokedex

I hope you had a fun time building this app with me. Feel free to ping me for questions and yes, you can download the source here. Just run npm install and npm start to start the app.

Discover and read more posts from Bhargav
get started
Enjoy this post?

Leave a like and comment for Bhargav

26Replies
José Roberto Lira
2 months ago

I can not make the containers where the mouse-pointer is a modal, can you help me?

Muhammet Aydin
6 months ago

If you were to deploy this app to heroku would there be some additional steps to take for it to work.

Bhargav
6 months ago

Depends on the purpose. If you are deploying a similar app to production, it makes sense to have production config with webpack, so you can optimize and compress your builds. Also, you can optimise some of the code by using require.ensure, which is dynamic chunk import facility and imports are optimised over HTTP.

Morten Saabye Kristensen
6 months ago

Why can’t i get a bigger object from the api? i checked the documentation and api/v2/pokemon{id} should get a way bigger object…

Bhargav
6 months ago

Hmmm, interesting. Perhaps there was a change in the api recently. What changes do you see ?

Morten Saabye Kristensen
6 months ago

the documentation (http://pokeapi.co/docsv2/#p…) just shows that i should be able to get things like height, weight and abilities, but i don’t understand why i’m not getting it then… Note that i am very new to using REST api’s

EDIT: Just looked at it again… I guess i have to make a new api call to get data about a specific pokemon… e.g. /api/v2/pokemon/1 gets the whole bulbasaur object…

Bhargav
a month ago

Exactly. We are using the list api right now it is light weight. To fetch more details, you need to use the other endpoints. Good luck!

Show more replies

Get curated posts in your inbox

Read more posts to become a better developer