Small Project Reflection: Natural Pokedex (React)

misnina

Nina

Posted on March 30, 2019

Small Project Reflection: Natural Pokedex (React)

Edit: I fixed some issues, which I detail here

Project Here

More Pokemon! 100 Days of Code Days 11 - 13

I probably should have done a bit bigger project by now, but I like the idea suggested to me by my friend, that I do the pokedex of the hack(?) we're working on called Gnosis! (Is it still a hack when it's using the decomp & disassembly of emerald?) These are the native pokemon you can find in the Tenso region. There are some different forms and mega evolutions, but for the purpose of this dex we're just pulling from the vanilla entries once again from PokéAPI. While there is a lot of data that I can pull from, I decided since some will vary in a new game, I would just pull the natural qualities and typing from them. Other than that my only goal is to be able to 'search' for related entries, so let's get started!

Displaying Sprites

At first I wanted to use this resource in it's entirety, but I couldn't quite figure it out, so I just ended up grabbing the main list of named menu sprites for the pokemon to display. I'm really picky about having everything match, and since the transition to 3D models, sprite resources and split between the 3D models and the old sprites, while PokeApi links to a sprite resource that uses both. There's also fanmade resources to spriteify them all, but quality varies. That's just no good to me, so the one consistent image source for Pokemon is the menu sprites, no matter if the game is in 3D or not. Here's hoping Sword and Shield don't break the trend!

At this stage, I just have an array of the 200 pokemon in the dex. I iterate over my entry component 200 times, and store them in an array of JSX elements. This will attempt to rerender in it's lifecycle, so instead of pushing to the array, we set the specific instance based on it's ID. In the entry we have a PKMNSprite component that just grabs the related name array to it's ID, and uses that as a name for the .pngs of the sprites I saved.

    for (let i = 0; i < 200; i++) {
      entries[i] =
        <PKDEXEntry
          pkmnID={i}
          key={`entry-${i + 1}`}
        />
    }
...
export default function PKMNSprite(props) {
  return <img
    src={`${process.env.PUBLIC_URL}/regular/${GPOKEDEX[props.pkmnID].toLowerCase()}.png`}
    alt={GPOKEDEX[props.pkmnID]}
  />
}

Enter fullscreen mode Exit fullscreen mode

Getting from PokéAPI

While it may not be ideal, I've let the child component handle the request for information. The reason this isn't ideal as it was fine when it was just the entry getting it's own information, I have to report back to the App component about what information was received for the search criteria. More on that later.

Omitting some of the functions for controlling the parent's state, this is what our API call looks like:

   axios.get(`https://pokeapi.co/api/v2/pokemon/${name.toLowerCase()}`)
      .then(res => {
        this.setState({ info: res.data });
        return axios.get(res.data.species.url);
      }).then(res => {
        this.setState({ species: res.data });
        this.setState({ loading: false });
      }).catch(err => {
        console.log(name + err);
      })
  }
Enter fullscreen mode Exit fullscreen mode

PokéAPI stores it's general information in pages just for the forms of each pokemon, then goes into specifics about the species in another place, which is nicely linked from the information page. Note: The code will continue even if setState is still loading in. For this reason I use the response data to find the species url instead of this.state.info. The annoying part is however, the pokemon who have different forms and don't have a 'regular' state, put it in their name for the API call. So, I can't have it be as elegant as I want and have to add an exception before the call.

    let name = "bulbasaur";
    if (GPOKEDEX[this.props.pkmnID] === "Minior") {
      name = "minior-red-meteor";
    } else if (GPOKEDEX[this.props.pkmnID] === "Aegislash") {
      name = "aegislash-shield";
    } else {
      name = GPOKEDEX[this.props.pkmnID];
    }
Enter fullscreen mode Exit fullscreen mode

At this point, I just had the colors alternate for entries. Later I would make them be connected to their color in the pokedex.

Searching

Just displaying the dex is easy enough, but I wanted to be able to group the pokemon by similar attributes. This is where things get a little messy. The initial entries components just contain the blank components with no data about what's actually in the entries.

The PKMNEntry component sends back the info from it's calls back to the parent App Component. We create two variables to store the info data and the species entries and pass these functions down as props to the Entry to fill out once it collects it's data. I could have also only made it send the data I need instead of two arrays for the two calls, but I didn't feel like that would have changed much in the end.

  setInfoEntry = (pkmnID, data) => {
    infoEntries[pkmnID] = data;
  }

  setSpeciesEntry = (pkmnID, data) => {
    speciesEntries[pkmnID] = data;
  }
Enter fullscreen mode Exit fullscreen mode

A problem I was having is that if you try and use the buttons to search before the page has loaded all of the data, it errors out as some of the search terms are undefined. For this reason, the PKMNEntry components are loaded, but hidden, while the entire page is on a loading screen, then once the 200th pokemon loading entry in a loading array is set to true it then loads the entries properly. This is unfortunate as it loads twice, but I wasn't sure the best way around besides calling axios in the parents for each entry and passing along as props. It's a case of where you want your data to be. I might refactor it to do so, I'm not sure right now.

Once we have all the data in two arrays, the search functions bound to the parent are passed down as props into the PKMNStats Component and added to buttons. This specific one will take the color that is was given and input it into this function. We clear any previous filtered entries first, then iterate through our 200 entries array, looking to see if they match the path in the relevant information array. For this reason, search types are all different functions. I feel I could have made one function that accounted for them all, but it would be a mess of if statements for each condition, so I'd rather have them be separate.

  showColorEntries = (color) => {
    filteredEntries = [];
    entries.forEach((entry, i) => {
      if (speciesEntries[i].color.name === color) {
        filteredEntries.push(entry);
      }
    })
    this.setState({ showAll: false, showFiltered: true });
    this.setPageFilter('Color', color);
  }
Enter fullscreen mode Exit fullscreen mode

I then swap if either the all entries, or the filtered entries are showing. A button at the top will take us back to all entries so we can try again. In the end the design was pretty simple, I didn't feel the need to make it so complex as we're just trying to display easy and quick data.

Conclusion

I really need to do a more medium sized project, but it's hard to find something interesting that hasn't been done. I say that, but my last to-do app failed spectacularly so I still need to redo something as supposedly simple and overdone as that. I'm happy with this though, I like thinking about the natural attributes of the pokemon instead of just the relevent stats. Finding all the squiggly pokemon and reading the hilariously terrifying dex entries is always a joy!

💖 💪 🙅 🚩
misnina
Nina

Posted on March 30, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Learning React State
webdev Learning React State

March 8, 2024

How rendering works: The commit phase
beginners How rendering works: The commit phase

February 13, 2024

React Hooks for Beginners
react React Hooks for Beginners

December 5, 2021