0% found this document useful (0 votes)
85 views45 pages

Miguel Silva - React Curse Continuation Part2 in Markdown

This document discusses React Router, including its primary components like Router, Route, Link, and Switch. It provides examples of setting up routing in a React app, including nested routes and passing route parameters. Key points covered include conditionally rendering UI based on routes, using Link to navigate between routes, and accessing route parameters and location via hooks like useParams and useLocation.

Uploaded by

Miguel Silva
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
85 views45 pages

Miguel Silva - React Curse Continuation Part2 in Markdown

This document discusses React Router, including its primary components like Router, Route, Link, and Switch. It provides examples of setting up routing in a React app, including nested routes and passing route parameters. Key points covered include conditionally rendering UI based on routes, using Link to navigate between routes, and accessing route parameters and location via hooks like useParams and useLocation.

Uploaded by

Miguel Silva
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 45

# 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&param2=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

You might also like