Miguel Silva - React Curse Continuation Part2 in Markdown
Miguel Silva - React Curse Continuation Part2 in Markdown
## React Router
[Documentation](https://reactrouter.com/web/guides/quick-start)
* Declarative API
* Hooks
* Routers
* BrowserRouter
* Hashrouter
* Route Matches
* Route
* Switch
* Link
* NavLink
* Redirect
#### Hooks
* useHistory
* useLocation
* useParams
### BrowserRouter
`rpm i react-router-dom`
BrowserRouter is a contextProvider
```jsx
// index.js
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById("root")
```
Everytime we want to link a specific part of the website we need to use Link
```jsx
return (
<div>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</div>
```
So clicking each button will change current URL to site/ & site/about
Version 1:
```jsx
function App() {
return (
<div>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Switch>
</Switch>
</div>
```
This previous example won't work with About because what React does is to test which is the first route
that begin with "/"
```jsx
<Switch>
</Switch>
```
Second way, using *exact* prop:
```jsx
function App() {
return (
<div>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Switch>
</Switch>
</div>
```
```jsx
// App.js
function App() {
return (
<div>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Switch>
</Switch>
</div>
```
But for example if you need to pass properties to the component you need to use render
```jsx
```
#### Newest Way
```jsx
<Switch>
</Switch>
```
---
```jsx
// index.js
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById("root")
```
```jsx
// App.js
function App() {
return (
<div>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
</nav>
<Switch>
<Home/>
</Route>
<Route path="/about">
<About/>
</Route>
<Route path="/contact">
<Contact/>
</Route>
</Switch>
</div>
```
---
```jsx
```
```jsx
```
```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
```
* useLocation
* useParams
* useHistory
* useRouteMatch
```jsx
// ...
<script src="/index.pack.js"></script>
```
We want to use
* /profile/settings
* /profile/info
```jsx
// App.js
```
```jsx
// Profile.js
function Profile() {
return (
<div>
<h1>Profile Page</h1>
<ul>
</ul>
<Switch>
<Route path="/profile/info">
<Info/>
</Route>
<Route path="/profile/settings">
<Settings/>
</Route>
</Switch>
</div>
)
}
```
```jsx
// App.js
function App() {
return (
<div>
<Header />
<Switch>
<Home />
</Route>
<Route path="/profile">
<Profile/>
</Route>
</Switch>
<Footer />
</div>
```
*Note. Here we have to use exact path for /profile because it starts the same as /Profile/info
```jsx
// Profile
function Profile() {
return (
<div>
<h1>Profile Page</h1>
<ul>
</ul>
<Switch>
<Route path="/profile/info">
<Info/>
</Route>
<Route path="/profile/settings">
<Settings/>
</Route>
</Switch>
</div>
```
```jsx
// App.js
function App() {
return (
<div>
<Header />
<Switch>
<Home />
</Route>
<Profile />
</Route>
<Route path="/profile/info">
<Info/>
</Route>
<Route path="/profile/settings">
<Settings/>
</Route>
</Switch>
<Footer />
</div>
```
### useParams
like
`site.com/recipes/1`
```json
/*servicesData.js*/
export default [
"price": 30,
"_id": "1",
},
"price": 50,
"_id": "2",
"description": "Remove those pesky dead tree parts from your property."
},
"name": "Weeding",
"price": 50,
"_id": "3",
},
"price": 100,
"_id": "4",
```
```jsx
// ServicesList.js
function ServicesList() {
<h3 key={service._id}>
</h3>
))
return (
<div>
{services}
</div>
```
```jsx
// App.js
function App() {
return (
<div>
<Header />
<Switch>
<Home />
</Route>
<ServicesList />
</Route>
<ServiceDetail />
</Route>
</Switch>
</div>
```
```jsx
// ServiceDetails.js
function ServiceDetail(props) {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
return (
<div>
<h3>{thisService.name} - ${thisService.price}</h3>
<p>{thisService.description}</p>
</div>
```
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
// path: "/user",
// url: "/user",
// isExact: true,
// params: {}
// }
```jsx
// Profile.js
function Profile() {
// https://reacttraining.com/blog/react-router-v5-1/#useroutematch
return (
<div>
<h1>Profile Page</h1>
<ul>
</ul>
<Switch>
<Route path={`${path}/info`}>
<Info/>
</Route>
<Route path={`${path}/settings`}>
<Settings/>
</Route>
</Switch>
</div>
```
```jsx
// App.js
function App() {
return (
<div>
<Header />
<Switch>
<Home />
</Route>
<Route path="/profile">
<Profile/>
</Route>
</Switch>
<Footer />
</div>
```
---
### 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("")
push(path, state)
```jsx
// ServiceDetail.js
function ServiceDetail() {
console.log("Submitting...")
setTimeout(() => {
history.push("/services")
}, 2000)
history.replace("")
return (
<div>
<h3>{thisService.name} - ${thisService.price}</h3>
<p>{thisService.description}</p>
</div>
```
### useLocation
```json
{pathname: "/services", search: "", hash: "", state: null, key: "qyfp8w"}
```
* /services
* /services/1
* search: queryString ex. "?param1=hello¶m2=world"
* state: send a message to ReactRouter. Letting the component where it came from.
---
```jsx
// App.js
function App() {
return (
<div>
</div>
```
Case of use:
```jsx
// App.js
function App() {
return (
<div>
<Link to="/">Home</Link>
<Link to="/private">Private</Link>
<Switch>
</Route>
<Route path="/private">
isLoggedIn ?
</Route>
<Route path="/login">
</Route>
</Switch>
</div>
}
export default App
```
---
## Capstone Project
A e-commerce site
Image attributions:
https://picsum.photos/
https://unsplash.com/
https://github.com/bobziroll/scrimba-react-bootcamp-images
Icon library
https://remixicon.com/
Libraries
* PropTypes - https://reactjs.org/docs/typechecking-with-proptypes.html
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)
# Challenge
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)
```jsx
// Context.js
function ContextProvider({children}) {
return (
<Context.Provider value="">
{children}
</Context.Provider>
```
```jsx
// Index.js
ReactDOM.render(
<ContextProvider>
<Router>
<App />
</Router>
</ContextProvider>,
document.getElementById("root")
```
```jsx
// context.js
function ContextProvider({children}) {
return (
<Context.Provider value={{allPhotos}}>
{children}
</Context.Provider>
```
---
```jsx
// Context.js
function ContextProvider({children}) {
useEffect(() => {
fetch(url)
}, [])
console.log(allPhotos)
return (
<Context.Provider value={{allPhotos}}>
{children}
</Context.Provider>
```
remember that when state changes, a functional component gets called again
---
*note. Try to use id of data when creating a list of compoennts for index
```jsx
// Photos.js
function Photos() {
))
return (
<main className="photos">
{imageElements}
</main>
```
```jsx
// Image.js
return (
</div>
```
```jsx
// Image.js
return (
<div
className={`${className} image-container`}
onMouseEnter={() => setHovered(true)}
>
</div>
```
---
Conditonal rendering
```jsx
// Image.js
return (
<div
className={`${className} image-container`}
>
<img src={img.url} className="image-grid"/>
{heartIcon}
{cartIcon}
</div>
```
```jsx
hovered ?
<>
<Element1 />
<Element2 />
</> :
null
</div>
```
---
```jsx
// Context.js
function ContextProvider({children}) {
useEffect(() => {
fetch(url)
}, [])
function toggleFavorite(id) {
console.log(id)
console.log(!photo.isFavorite)
return photo
})
setAllPhotos(updatedArr)
return (
</Context.Provider>
```
```jsx
// Image.js
return (
<div
className={`${className} image-container`}
>
{cartIcon}
</div>
```
---
```jsx
// Image.js
function heartIcon() {
if(img.isFavorite) {
} else if(hovered) {
<div
className={`${className} image-container`}
>
{heartIcon()}
{cartIcon}
</div>
```
---
```jsx
// Image.js
Image.propTypes = {
className: PropTypes.string,
img: PropTypes.shape({
id: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
isFavorite: PropTypes.bool
})
```
---
```jsx
// Context.js
function ContextProvider({children}) {
useEffect(() => {
fetch(url)
}, [])
function toggleFavorite(id) {
return photo
})
setAllPhotos(updatedArr)
function addToCart(newItem) {
console.log(cartItems)
return (
{children}
</Context.Provider>
```
```jsx
// Image.js
function ContextProvider({children}) {
useEffect(() => {
fetch(url)
}, [])
function toggleFavorite(id) {
return photo
})
setAllPhotos(updatedArr)
function addToCart(newItem) {
console.log(cartItems)
return (
</Context.Provider>
```
---
```jsx
// Image.js
function heartIcon() {
if(img.isFavorite) {
} else if(hovered) {
}
function cartIcon() {
if(alreadyInCart) {
} else if(hovered) {
return (
<div
className={`${className} image-container`}
>
{heartIcon()}
{cartIcon()}
</div>
Image.propTypes = {
className: PropTypes.string,
img: PropTypes.shape({
id: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
isFavorite: PropTypes.bool
})
}
```
---