You've done some amazing work getting to this point! You've created your own Product component and we're now rendering products on the page dynamically. You also know how to change the state of a component. Now it's time to take a look at events and specifically, how we can emit an event to add a product to the cart.
You'll notice that we've got the 'Full details' and the 'Add to cart' buttons, but neither of them work because we haven't wired them up. Let's rectify that and wire up the Add to cart button so it adds the Product to the cart.
In your Product
component add an event listener and event handler called addToCart
to the <button>
element. When we click on the button we'll see that product logged in the console. Below are the code snippets. Use the Img
component as a reference for how to add this to your Product
component.
const addToCart = (product) => {
console.table(product)
}
<button onClick={addToCart(props.product)}>
Add to cart
</button>
Now we have a problem to solve. In the data that we import from our products.json
file there is a property called 'cart' that is an Array. We need to push the product into the cart array, but the cart exists in the data object that belongs to the App
component that is outside the scope of our Product
component.
There are a number of ways to solve this and two examples are:
App
that the child calls with a value from it's own scope.Using Redux at this point is a little overkill as there are only three components in our app. For that reason, let's pass a function down the component tree into the child component from the parent. We'll incorporate Redux later when our app becomes more complex
Let's visualise the process in a diagram:
Data Flowing Down | Passing Data Up |
---|---|
So far we have only passed data down the component tree in one direction. Now we are going to go in the opposite direction and pass data up the component tree. To do this we are going to pass our parent setState function into a child component for it to call with the values it has within it.
Create a reactive variable within the App
component that will be the cart.
import { useState } from 'react'
import data from './products.json'
import { Product } from './Product'
function App() {
const [cart, setCart] = useState(data.cart)
const addToCart = product => {
setCart([...cart, product])
}
console.log(cart)
return (
<div className="App">
{data.products.map(product => <Product product={product} addToCart={addToCart} />)}
</div>
);
}
export default App
Our updates are:
useState
from 'react'cart
and setter called setCart
addToCart
function that will receive a product and add it to the array of items already in the cartaddToCart
function down into the Product
componentNow we can call this within the Product
component like this. You can remove the Product
component's version of addToCart
, we are calling the addToCart
function passed down to us from the App
parent component with our instance of a product.
<button onClick={()=>props.addToCart(props.product)}>Add to cart</button>
Does it work?
The problem we have now is that you can continue to click the button and the product will get added to the cart array over and over again.
Modify the addToCart
method in your app component so that it checks whether the product already exists in the cart array.
The product data object has a property called addedToCart
which is a Boolean. Can you insure this is updated as the item is added/removed from the cart
If an item is in the cart the button should show a different label instead of 'Add to cart' it should say 'Remove from cart'
Clicking on the 'Remove from cart' button should remove that product from the cart