# The React Bootcamp part2
## React Router
### Intro to React Router
[Documentation](https://reactrouter.com/web/guides/quick-start)
* Conditionally render large parts of your page
* Declarative API
* Hooks
#### Primary Components
* Routers
* BrowserRouter
* Hashrouter
* Route Matches
* Route
* Switch
* Navigation ("route changes")
* Link
* NavLink
* Redirect
#### Hooks
* useHistory
* useLocation
* useParams
### BrowserRouter
In order to use React Router you need to install it:
`rpm i react-router-dom`
BrowserRouter is a contextProvider
```jsx
// index.js
import React from "react"
import ReactDOM from "react-dom"
import {BrowserRouter as Router} from "react-router-dom"
import App from "./App"
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById("root")
```
People tend to rename BrowserRouter as Router
### React Router - Link
Everytime we want to link a specific part of the website we need to use Link
Linke uses a prop called `to` for indicating the route
```jsx
import React from "react"
import {Link} from "react-router-dom"
function App() {
return (
<div>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</div>
export default App
```
So clicking each button will change current URL to site/ & site/about
Inside Link it renders an ancher (`<a>`)
### Switch & Route
Currently in this version of the tutorial version of react-router-dom is 5.1.2
there are 3 different ways to use Route:
Version 1:
```jsx
import React from "react"
import {Link, Switch, Route} from "react-router-dom"
function App() {
return (
<div>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Switch>
<Route path="/" render={() => <h1>Home Page!</h1>} />
<Route path="/about" render={() => <h1>About Page!</h1>} />
</Switch>
</div>
export default App
```
This previous example won't work with About because what React does is to test which is the first route
that begin with "/"
because About starts with it ("/about")
so it will still redirect to "/".
One way to correct it is altering order in links:
```jsx
<Switch>
<Route path="/" render={() => <h1>Home Page!</h1>} />
<Route path="/about" render={() => <h1>About Page!</h1>} />
</Switch>
```
Second way, using *exact* prop:
```jsx
import React from "react"
import {Link, Switch, Route} from "react-router-dom"
import Home from "./Home"
import About from "./About"
function App() {
return (
<div>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Switch>
<Route exact path="/" render={() => <Home />} />
<Route path="/about" render={() => <About />} />
</Switch>
</div>
export default App
```
So with exact you don't need to care about order
Instead of using render you can use component prop:
```jsx
// App.js
import React from "react"
import {Link, Switch, Route} from "react-router-dom"
import Home from "./Home"
import About from "./About"
function App() {
return (
<div>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</Switch>
</div>
export default App
```
But for example if you need to pass properties to the component you need to use render
```jsx
<Route exact path="/" render={() => <About some="value"/>} />
```
#### Newest Way
This is now the used method:
You can use children:
```jsx
<Switch>
<Route exact path="/"><Home /></Route>
<Route path="/about"><About some="value" /></Route>
</Switch>
```
So you can pass any props and it's cleaner
---
### React Router Example2
First you have to wrap the App in a Router:
```jsx
// index.js
import React from "react"
import ReactDOM from "react-dom"
import {BrowserRouter as Router} from "react-router-dom"
import App from "./App"
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById("root")
```
```jsx
// App.js
import React from "react"
import {Switch, Route, Link} from "react-router-dom"
import Home from "./Home"
import About from "./About"
import Contact from "./Contact"
function App() {
return (
<div>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
</nav>
<Switch>
<Route exact path="/">
<Home/>
</Route>
<Route path="/about">
<About/>
</Route>
<Route path="/contact">
<Contact/>
</Route>
</Switch>
</div>
export default App
```
---
### props passed to routed components
When you use:
```jsx
<Router exact path="/" component={Home} />
```
React passes a lot of props to the Home component.
You can receive those props using:
```jsx
<Router exact path="/" render={props => console.log(props)} />
```
Example of what are those props:
```json
{history: {length: 32, action: "POP", location: {pathname: "/", search: "", hash: "", state: null}, createHref:
createHref(location), push: push(path, state), replace: replace(path, state), go: go(n), goBack: goBack(),
goForward: goForward(), block: block(prompt), listen: listen(listener)}, location: {pathname: "/", search:
"", hash: "", state: null}, match: {path: "/", url: "/", isExact: true, params: {}}, staticContext: null}
```
These props are not passed to component using children way:
```jsx
<Router exact path="/"><Home /></Router>
```
But you can access them using hooks like
* useLocation
* useParams
* useHistory
* useRouteMatch
### Scrimba Error with nested routes
If you refresh browser in Scrimba using a nested route example "/some/info"
you will get an error:
`›Error: No such file: profile/index``
You need to add / at the beginning of refs in:
```jsx
<link rel="stylesheet" href="/styles.css">
// ...
<script src="/index.pack.js"></script>
```
### Nested Routes
We want to use
* /profile/settings
* /profile/info
```jsx
// App.js
```
```jsx
// Profile.js
import React from "react"
import {Link, Switch, Route} from "react-router-dom"
import Settings from "./Settings"
import Info from "./Info"
function Profile() {
return (
<div>
<h1>Profile Page</h1>
<ul>
<li><Link to="/profile/info">Profile Info</Link></li>
<li><Link to="/profile/settings">Profile Settings</Link></li>
</ul>
<Switch>
<Route path="/profile/info">
<Info/>
</Route>
<Route path="/profile/settings">
<Settings/>
</Route>
</Switch>
</div>
)
}
export default Profile
```
```jsx
// App.js
import React from "react"
import Header from "./components/Header"
import Footer from "./components/Footer"
import Home from "./pages/Home"
import Profile from "./pages/profile/Profile"
import {Switch, Route} from "react-router-dom"
function App() {
return (
<div>
<Header />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/profile">
<Profile/>
</Route>
</Switch>
<Footer />
</div>
export default App
```
If we want to profile and info replace the whole content:
*Note. Here we have to use exact path for /profile because it starts the same as /Profile/info
```jsx
// Profile
import React from "react"
import {Link, Switch, Route} from "react-router-dom"
import Settings from "./Settings"
import Info from "./Info"
function Profile() {
return (
<div>
<h1>Profile Page</h1>
<ul>
<li><Link to="/profile/info">Profile Info</Link></li>
<li><Link to="/profile/settings">Profile Settings</Link></li>
</ul>
<Switch>
<Route path="/profile/info">
<Info/>
</Route>
<Route path="/profile/settings">
<Settings/>
</Route>
</Switch>
</div>
export default Profile
```
```jsx
// App.js
import React from "react"
import Header from "./components/Header"
import Footer from "./components/Footer"
import Home from "./pages/Home"
import Profile from "./pages/profile/Profile"
import Info from "./pages/profile/Info"
import Settings from "./pages/profile/Settings"
import {Switch, Route} from "react-router-dom"
function App() {
return (
<div>
<Header />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route exact path="/profile">
<Profile />
</Route>
<Route path="/profile/info">
<Info/>
</Route>
<Route path="/profile/settings">
<Settings/>
</Route>
</Switch>
<Footer />
</div>
export default App
```
### useParams
The value of the params are going to be string
To use params in URL
like
`site.com/recipes/1`
```json
/*servicesData.js*/
export default [
"name": "Lawn Mowing",
"price": 30,
"_id": "1",
"description": "Have a carpet-like lawn without any work."
},
"name": "Leaf Raking",
"price": 50,
"_id": "2",
"description": "Remove those pesky dead tree parts from your property."
},
"name": "Weeding",
"price": 50,
"_id": "3",
"description": "Don't let the invaders ruin your yard."
},
"name": "Sprinkler repair",
"price": 100,
"_id": "4",
"description": "Keep your irrigation system top-notch."
}
```
```jsx
// ServicesList.js
import React from "react"
import servicesData from "./servicesData"
import {Link} from "react-router-dom"
function ServicesList() {
const services = servicesData.map(service => (
<h3 key={service._id}>
<Link to={`/services/${service._id}`}>{service.name}</Link> - ${service.price}
</h3>
))
return (
<div>
<h1>Services List Page</h1>
{services}
</div>
export default ServicesList
```
```jsx
// App.js
import React from "react"
import Header from "./components/Header"
import Home from "./pages/Home"
import ServicesList from "./pages/services/ServicesList"
import ServiceDetail from "./pages/services/ServiceDetail"
import {Switch, Route} from "react-router-dom"
function App() {
return (
<div>
<Header />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route exact path="/services" >
<ServicesList />
</Route>
<Route path="/services/:serviceId" >
<ServiceDetail />
</Route>
</Switch>
</div>
export default App
```
```jsx
// ServiceDetails.js
import React from "react"
import {useParams} from "react-router-dom"
import servicesData from "./servicesData"
function ServiceDetail(props) {
const {serviceId} = useParams()
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
const thisService = servicesData.find(service => service._id === serviceId)
return (
<div>
<h1>Service Detail Page</h1>
<h3>{thisService.name} - ${thisService.price}</h3>
<p>{thisService.description}</p>
</div>
export default ServiceDetail
```
So useParams let us to access an object mapped with the paramNames and the values
---
### useRouteMatch
[Documentation](https://reacttraining.com/blog/react-router-v5-1/#useroutematch)
grab information abot how React Match this route
useRouteMatch give us this:
// path: "/user",
// url: "/user",
// isExact: true,
// params: {}
// }
They will allow us to create paths in a dynamic way.
You should use url to match paths in Link component.
So you can write:
```jsx
// Profile.js
import React from "react"
import {Link, Switch, Route, useRouteMatch} from "react-router-dom"
import Settings from "./Settings"
import Info from "./Info"
function Profile() {
const {path, url} = useRouteMatch()
// https://reacttraining.com/blog/react-router-v5-1/#useroutematch
return (
<div>
<h1>Profile Page</h1>
<ul>
<li><Link to={`${url}/info`}>Profile Info</Link></li>
<li><Link to={`${url}/settings`}>Profile Settings</Link></li>
</ul>
<Switch>
<Route path={`${path}/info`}>
<Info/>
</Route>
<Route path={`${path}/settings`}>
<Settings/>
</Route>
</Switch>
</div>
export default Profile
```
```jsx
// App.js
import React from "react"
import Header from "./components/Header"
import Footer from "./components/Footer"
import Home from "./pages/Home"
import Profile from "./pages/profile/Profile"
import {Switch, Route} from "react-router-dom"
function App() {
return (
<div>
<Header />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/profile">
<Profile/>
</Route>
</Switch>
<Footer />
</div>
export default App
```
---
### useHistory
```json
{length: 13, action: "PUSH", location: {pathname: "/services/2", search: "", hash: "", state: null, key:
"nulft8"}, createHref: createHref(location), push: push(path, state), replace: replace(path, state), go:
go(n), goBack: goBack(), goForward: goForward(), block: block(prompt), listen: listen(listener)}
```
For example to use redirect logic like based on condition redirect to different routes
useHistory
useful push,
* replace("")
* goBack(n): go back 1 page in history. n is the number of steps (optional)
push is useful to redirect
push(path, state)
```jsx
// ServiceDetail.js
import React from "react"
import {useParams, useHistory} from "react-router-dom"
import servicesData from "./servicesData"
function ServiceDetail() {
const {serviceId} = useParams()
const history = useHistory()
const thisService = servicesData.find(service => service._id === serviceId)
function handleClick() {
console.log("Submitting...")
setTimeout(() => {
history.push("/services")
}, 2000)
history.replace("")
return (
<div>
<h1>Service Detail Page</h1>
<h3>{thisService.name} - ${thisService.price}</h3>
<p>{thisService.description}</p>
<button onClick={handleClick}>Go back to all services</button>
</div>
export default ServiceDetail
```
### useLocation
useLocation give us:
```json
{pathname: "/services", search: "", hash: "", state: null, key: "qyfp8w"}
```
* pathname where I am in the application
* /services
* /services/1
* search: queryString ex. "?param1=hello¶m2=world"
* state: send a message to ReactRouter. Letting the component where it came from.
* So you can say that user comes from this part
---
### Redirect Component
```jsx
// App.js
import React from "react"
import {Redirect} from "react-router-dom"
function App() {
return (
<div>
<Redirect to="/whatever" />
</div>
export default App
```
We immediately get redirected to /whatever
You can use authentication with this
Case of use:
```jsx
// App.js
import {Link, Switch, Route, Redirect} from "react-router-dom"
// https://reacttraining.com/react-router/web/example/auth-workflow
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false)
return (
<div>
<Link to="/">Home</Link>
<Link to="/private">Private</Link>
<Switch>
<Route exact path="/">
<h1>Home page, anyone is allowed here</h1>
</Route>
<Route path="/private">
isLoggedIn ?
<h1>Protected page, must be logged in to be here</h1> :
<Redirect to="/login" />
</Route>
<Route path="/login">
<button onClick={() => setIsLoggedIn(true)}>Log in</button>
</Route>
</Switch>
</div>
}
export default App
```
[More complex example](https://reacttraining.com/react-router/web/example/auth-workflow)
---
## Capstone Project
A e-commerce site
Image attributions:
https://picsum.photos/
https://unsplash.com/
GitHub repo of images and JSON file
https://github.com/bobziroll/scrimba-react-bootcamp-images
Icon library
https://remixicon.com/
Libraries
* React Router - https://reacttraining.com/react-router/web/guides/quick-start
* PropTypes - https://reactjs.org/docs/typechecking-with-proptypes.html
### Icons URL
Old URL
https://cdn.remixicon.com/releases/v2.1.0/remixicon.css
New URL
https://cdn.jsdelivr.net/npm/[email protected]/fonts/remixicon.css
### React Router Setup
You can create a folder pages to put the components that will be used as pages in router (routes)
### Context Setup
# Challenge
Set up the Context for our app.
1. In a new file, create a new context with React
2. In that same file, create a custom component that renders the Provider of the context you created
3. For now, just pass in an empty string "" as the context provider's value prop
4. Export the custom Provider component and the full context object (so we can pass it to the
useContext hook eventually)
5. Set up your index.js to use the custom context Provider you created. (You can wrap it as a parent of
the Router component)
Something that is very common is desconsturcuring things in props:
```jsx
// Context.js
import React from "react"
const Context = React.createContext()
function ContextProvider({children}) {
return (
<Context.Provider value="">
{children}
</Context.Provider>
export {ContextProvider, Context}
```
```jsx
// Index.js
import React from "react"
import ReactDOM from "react-dom"
import {BrowserRouter as Router} from "react-router-dom"
import {ContextProvider} from "./Context"
import App from "./App"
ReactDOM.render(
<ContextProvider>
<Router>
<App />
</Router>
</ContextProvider>,
document.getElementById("root")
```
### Context State
Add state to context:
the state will be the array of photos, first it will be empty
```jsx
// context.js
import React, {useState} from "react"
const Context = React.createContext()
function ContextProvider({children}) {
const [allPhotos, setAllPhotos] = useState([])
return (
<Context.Provider value={{allPhotos}}>
{children}
</Context.Provider>
export {ContextProvider, Context}
```
---
### Fetch photos
Get json photos from API
As soon as contextProvider renders get the JSON data:
```jsx
// Context.js
import React, {useState, useEffect} from "react"
const Context = React.createContext()
function ContextProvider({children}) {
const [allPhotos, setAllPhotos] = useState([])
const url = "https://raw.githubusercontent.com/bobziroll/scrimba-react-bootcamp-
images/master/images.json"
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => setAllPhotos(data))
}, [])
console.log(allPhotos)
return (
<Context.Provider value={{allPhotos}}>
{children}
</Context.Provider>
export {ContextProvider, Context}
```
remember that when state changes, a functional component gets called again
---
### Map Photos
Using data in the context state, map over it in the Photos page
*note. Try to use id of data when creating a list of compoennts for index
```jsx
// Photos.js
import React, {useContext} from "react"
import Image from "../components/Image"
import {Context} from "../Context"
import {getClass} from "../utils"
function Photos() {
const {allPhotos} = useContext(Context)
const imageElements = allPhotos.map((img, i) => (
<Image key={img.id} img={img} className={getClass(i)} />
))
return (
<main className="photos">
{imageElements}
</main>
export default Photos
```
```jsx
// Image.js
import React from "react"
function Image({className, img}) {
return (
<div className={`${className} image-container`}>
<img src={img.url} className="image-grid"/>
</div>
export default Image
```
### Track Hover State
Each Image needs to manage state.
You need to save in state if image is hovered or not (mouseOver or mouseOut)
```jsx
// Image.js
import React, {useState} from "react"
function Image({className, img}) {
const [hovered, setHovered] = useState(false)
return (
<div
className={`${className} image-container`}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<img src={img.url} className="image-grid"/>
</div>
export default Image
```
---
### Display Icons on Hover
Conditonal rendering
```jsx
// Image.js
import React, {useState} from "react"
function Image({className, img}) {
const [hovered, setHovered] = useState(false)
const heartIcon = hovered && <i className="ri-heart-line favorite"></i>
const cartIcon = hovered && <i className="ri-add-circle-line cart"></i>
return (
<div
className={`${className} image-container`}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<img src={img.url} className="image-grid"/>
{heartIcon}
{cartIcon}
</div>
export default Image
```
Also you can do the conditional rendering like this:
```jsx
<img src={img.url} className="image-grid"/>
hovered ?
<>
<Element1 />
<Element2 />
</> :
null
</div>
```
---
### Toggle isFavorite
Remember that setState functions are asynchronous
```jsx
// Context.js
import React, {useState, useEffect} from "react"
const Context = React.createContext()
function ContextProvider({children}) {
const [allPhotos, setAllPhotos] = useState([])
const url = "https://raw.githubusercontent.com/bobziroll/scrimba-react-bootcamp-
images/master/images.json"
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => setAllPhotos(data))
}, [])
function toggleFavorite(id) {
const updatedArr = allPhotos.map(photo => {
if(photo.id === id) {
console.log(id)
console.log(!photo.isFavorite)
return {...photo, isFavorite: !photo.isFavorite}
return photo
})
setAllPhotos(updatedArr)
return (
<Context.Provider value={{allPhotos, toggleFavorite}}>
{children}
</Context.Provider>
export {ContextProvider, Context}
```
```jsx
// Image.js
import React, {useState, useContext} from "react"
import {Context} from "../Context"
function Image({className, img}) {
const [hovered, setHovered] = useState(false)
const {toggleFavorite} = useContext(Context)
const heartIcon = hovered &&
<i className="ri-heart-line favorite" onClick={() => toggleFavorite(img.id)}></i>
const cartIcon = hovered && <i className="ri-add-circle-line cart"></i>
return (
<div
className={`${className} image-container`}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<img src={img.url} className="image-grid"/>
{heartIcon}
{cartIcon}
</div>
export default Image
```
---
### Display favorite
```jsx
// Image.js
import React, {useState, useContext} from "react"
import {Context} from "../Context"
function Image({className, img}) {
const [hovered, setHovered] = useState(false)
const {toggleFavorite} = useContext(Context)
function heartIcon() {
if(img.isFavorite) {
return <i className="ri-heart-fill favorite" onClick={() => toggleFavorite(img.id)}></i>
} else if(hovered) {
return <i className="ri-heart-line favorite" onClick={() => toggleFavorite(img.id)}></i>
const cartIcon = hovered && <i className="ri-add-circle-line cart"></i>
return (
<div
className={`${className} image-container`}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<img src={img.url} className="image-grid"/>
{heartIcon()}
{cartIcon}
</div>
export default Image
```
---
### Image propTypes
It's a good habit to use propTypes in a component that receives props
```jsx
// Image.js
import PropTypes from "prop-types
Image.propTypes = {
className: PropTypes.string,
img: PropTypes.shape({
id: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
isFavorite: PropTypes.bool
})
```
---
### Add Image to Cart
```jsx
// Context.js
import React, {useState, useEffect} from "react"
const Context = React.createContext()
function ContextProvider({children}) {
const [allPhotos, setAllPhotos] = useState([])
const [cartItems, setCartItems] = useState([])
const url = "https://raw.githubusercontent.com/bobziroll/scrimba-react-bootcamp-
images/master/images.json"
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => setAllPhotos(data))
}, [])
function toggleFavorite(id) {
const updatedArr = allPhotos.map(photo => {
if(photo.id === id) {
return {...photo, isFavorite: !photo.isFavorite}
return photo
})
setAllPhotos(updatedArr)
function addToCart(newItem) {
setCartItems(prevItems => [...prevItems, newItem])
console.log(cartItems)
return (
<Context.Provider value={{allPhotos, toggleFavorite, addToCart}}>
{children}
</Context.Provider>
export {ContextProvider, Context}
```
```jsx
// Image.js
import React, {useState, useEffect} from "react"
const Context = React.createContext()
function ContextProvider({children}) {
const [allPhotos, setAllPhotos] = useState([])
const [cartItems, setCartItems] = useState([])
const url = "https://raw.githubusercontent.com/bobziroll/scrimba-react-bootcamp-
images/master/images.json"
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => setAllPhotos(data))
}, [])
function toggleFavorite(id) {
const updatedArr = allPhotos.map(photo => {
if(photo.id === id) {
return {...photo, isFavorite: !photo.isFavorite}
return photo
})
setAllPhotos(updatedArr)
function addToCart(newItem) {
setCartItems(prevItems => [...prevItems, newItem])
console.log(cartItems)
return (
<Context.Provider value={{allPhotos, toggleFavorite, addToCart}}>
{children}
</Context.Provider>
export {ContextProvider, Context}
```
---
### Indicate when image is in the cart
We pass also cartItems in context
```jsx
// Image.js
import React, {useState, useContext} from "react"
import PropTypes from "prop-types"
import {Context} from "../Context"
function Image({className, img}) {
const [hovered, setHovered] = useState(false)
const {toggleFavorite, addToCart, cartItems} = useContext(Context)
function heartIcon() {
if(img.isFavorite) {
return <i className="ri-heart-fill favorite" onClick={() => toggleFavorite(img.id)}></i>
} else if(hovered) {
return <i className="ri-heart-line favorite" onClick={() => toggleFavorite(img.id)}></i>
}
function cartIcon() {
const alreadyInCart = cartItems.some(item => item.id === img.id)
if(alreadyInCart) {
return <i className="ri-shopping-cart-fill cart"></i>
} else if(hovered) {
return <i className="ri-add-circle-line cart" onClick={() => addToCart(img)}></i>
return (
<div
className={`${className} image-container`}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<img src={img.url} className="image-grid"/>
{heartIcon()}
{cartIcon()}
</div>
Image.propTypes = {
className: PropTypes.string,
img: PropTypes.shape({
id: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
isFavorite: PropTypes.bool
})
}
export default Image
```
---
### Cart Page Setup