Over the next few weeks we’ll be building a digital Pokemon (simplified) card game. Complete with features like:
- Viewing and searching Pokemon “cards”
- Viewing and comparing stats
- Adding Pokemon cards to a deck
- Opportunities for you to expand functionality like dueling 2 Pokemon against each other.
- Fork this repo, clone to your desktop, and install dependencies:
npm install
- Start the server, and navigate to
localhost:1234
npm run start
- Lint
.js
and.jsx
filesnpm run lint
GOAL: Using the React Bootstrap CSS Framework and the pokeapi:
- Fetch and render a list of 150 Pokemon cards - each with limited information (picture, name, abilities).
- Add a search feature to find a specific Pokemon.
Use the following instructions to guide your coding:
- Get the Pokemon!
- Fetch using the provided
pokeApi
url and set the returned data on state. - NOTE: The API returns a
data.results
array of objects, each one containing just an ID, a Name, and a URL. That returned URL will be used to fetch the rest of the data about the Pokemon. Here is an exampleresponse
from the API.
- Fetch using the provided
- Iterate over the
pokemonList
array and for each pokemon render a single PokemonCard passing the name and url to the card. - Inside
PokemonCard
fetch the provided URL and set the returned data from this URL on state- Note the return of this URL is vastly different from the return of the above
pokeApi
URL. This URL will return all the data about a specific Pokemon. - For now, we’ll specifically want to pay attention to
sprites
andabilities
. - Here is an example of the response from this URL
- Note the return of this URL is vastly different from the return of the above
- Import a
Card
component from react-bootstrap, and render it, including the following pieces:Card.Img
- usepokemon.sprites.front_default
as the img’s srcCard.Title
with thename
of the pokemonCard.Text
with aul
of all of that pokemon’s abilities- At this point, we should see a list of all the pokemon cards in the UI, though it may be ugly and/or unstyled
- In
App.js
add anInputGroup
with aFormControl
to allow user to enter pokemon name (see react-bootstrap InputGroup docs) - Connect an
onChange
that will filter the list of pokemon- There’s a few ways to handle this, refer to previous exercises for inspiration or come up with another way on your own!
- At this point the list should be searchable
- Style the page a little bit more, try to center things and render the whole page in a
Container
and manyRow
/Col
- Save and push your changes back to your GitHub repository!
GOAL: By the end of this section, clicking on a single Pokemon should navigate to that pokemon’s Details page and display all of their stats!
- Continue from your Pokeverse repo that you used in the last section and checkout a new branch:
- To checkout a new branch, run the following command:
git checkout -b adds-react-router
- This will create a new branch named
”adds-react-router”
and then switch your local branch version to the new branch. When you’re done with this lesson feel free to merge your new branch intomain
or better yet - submit a Pull Request to your repo and review your code yourself and then merge it in! 🚀
- Install React Router:
npm i react-router-dom@6
- Create a new directory
src/routes/
and in it create a file calledHome.js
mkdir src/routes/
touch src/routes/Home.js
- Move almost everything out of App.js into routes/Home.js EXCEPT:
- The initial
useEffect
that fetches all of the pokemon - The
pokemonList
state - The container
div
and<Navigation />
- The initial
Home
should expect thepokemonList
array as a prop - and will handle the searching and filtering of thefilteredPokemon
array.- Create a new file
src/routes/PokemonDetails.js
. This component will display all of the pokemon's stats! We'll come back to this component later. - In
App
set up aBrowserRouter
that wraps everything that's left in the component. - Inside of the
<BrowserRouter>
, below<Navigation />
, add a<Routes>
component. - Inside of
Routes
, add 2 routes:- One with a path of
"/"
that renders the newly cratedHome
component and passespokemonList
to it - The other with a path of
"/:name"
that renders the newly created PokemonDetails component. Notice the:
before name, this is a special syntax for URL params, we'll be able to access the value of name later!
- One with a path of
- Inside of the
PokemonDetails
component:- Add a new state of pokemon with initial value of
null
- Import and call
useParams
. This is how we'll access thatname
param we added earlier!
const params = useParams();
- Inside of a
useEffect
,fetch
all of the details about this pokemon using the name from the params and thensetPokemon
with the fetched data.
https://pokeapi.co/api/v2/pokemon/${params.name}
- Since we'll be waiting for data to be fetched, we can conditionally render the page! To do this we can add 2 separate
return statements
. The first will be wrapped in an if statement
if (!pokemon) { return <>loading...</>; } return ( // ... the rest of the component here )
- Add a new state of pokemon with initial value of
- Now let's add all of the stats about the pokemon like such:
height: {pokemon.height}
weight: {pokemon.weight}
abilities: iterate over pokemon.abilities, and for each ability render the ability.ability.name
types: iterate over pokemon.types and for each type render the type.type.name
stats: iterate over pokemon.stats and for each stat render the stat.stat.name and stat.base_stat
- Finally, let's link to the newly created route and component! In
PokemonCard.js
, wrap the{name}
of the Pokemon with a<Link>
with a to value of/${name}
<Link to={`/${name}`}>
{name}
</Link>
- BONUS #1: Right now
PokemonDetails
is probably just plain text, and pretty boring looking. Since we already have React Bootstrap installed, check out theComponents
section of the React Bootstrap docs and see how you can style the page! - BONUS #2: Right now the All Pokemon link works like a normal anchor tag (when clicked the page loads/refreshes), but we want to use React Router, with no page refresh! Convert the
Nav.Link
to use the properties of theLink
component by passing it theas={Link}
prop, and switch out thehref
for a to prop. The react-bootstrap docs have examples of other components likeNav.Item
using theas
prop.
We will be updating our existing Pokeverse application with the following:
- Each pokemon card should have an “Add to Favorites” button.
- The pokemon that have been added to favorites can be viewed navigating to the new route,
/favorites
. - The favorites will be stored in context, in addition to
setFavorite
andremoveFavorite
functions to update the favorites list.
- Continue from your Pokeverse repo you already have and checkout a new branch:
git checkout -b adds-context
- When you’ve finished, merge your new branch into
main
or better yet - submit a Pull Request!
- In
FavoritesProvider.js
pass a value to theProvider
giving access tofavorites
,addFavorite
, andremoveFavorite
- In
App.js
wrap the returned component in theFavoritesProvider
imported fromFavoritesProvider.js
- Create a new file in routes called
Favorites.js
. For now you can just export an empty React component. We’ll come back to this file later. - Add a route to
/favorites
- Back in
App
add a new route with the path’/favorites’
and the element of<Favorites />
(of course, after importing Favorites). That’s it forApp.js
! - In
Navigation
add anotherNavLink
to’/favorites’
with the text of something like “My Favorites” or “My Deck”
- Back in
- Connect the addFavorite to PokemonCard!
- First, import
FavoritesContext
from ’FavoritesProvider’ as well asuseContext
from ’react’. - Destructure
addFavorite
out of context
const { addFavorite } = useContext(FavoritesContext);
- Import Button from Bootstrap
- Beneath the
<Card.Text>
section, add a<Button>
with anonClick
that when clicked callsaddFavorite
and pass the name to it.
<Button variant="primary" onClick={() => addFavorite(name)}> Add to Favorites </Button>
- First, import
- Back in
Favorites
, follow the same pattern from PokemonCard to pull out favorites from context.- Destructure favorites
- Map over favorites and for each favorite render a
<PokemonCard>
passing the prop ofname={favorite}
- TIP: To get the styling similar to what we have on the main page, take a look at how you’re rendering all of the cards in
Home.js
. It should be structured something like this:Container
>Row
>1 Col
for each PokemonCard
- Try navigating to
/favorites
now - you should see any pokemon that you clicked “Add to Favorites” here! You also may notice that you can click “Add to Favorites” over and over and get the same pokemon duplicated over and over. Take a look at the first item of the Bonus to fix that! - BONUS: After someone clicks “Add to Favorites”, they shouldn’t be able to click that button again. We already have a
removeFavorite
function available in Context that can be helpful here!- If a pokemon is already in
favorites
, instead of rendering an Add to Favorites button on the PokemonCard let’s render a Remove from Favorites button! - Try using a ternary for this
{someExpression ? ( <RenderThisThingIfSomeExpressionIsTruthy /> ) : ( <RenderThisThingIfSomeExpressionIsFalsey /> )} // or, in plain english: {ifThis ? ( then this ) : ( else this )}
- Try utilizing includes for this. If favorites includes name, then render an “Add to Favorites” button, else render a “Remove from Favorites” button.
- If a pokemon is already in
Made with
♥️ at Multiverse