In the single product page now we’ll add the “Add to cart” button, which once clicked will add the product into the shopping cart.

In the src/components/SingleProduct.js we add the button:

<button onClick={addToCart}>Add to cart</button>

which calls the addToCart method of the component when clicked:

const addToCart = () => {
  props.addToCart({ product: props.product, quantity: 1 })
}

which in turn calls the addToCart prop, passing the product object, and an integer that sets the quantity to be added to the cart.

This is the Route with the prop, in App:

<Route
  path="/product/:slug"
  render={({ match }) => (
    <SingleProduct
      product={products.find(p => p.slug === match.params.slug)}
      addToCart={addToCart}
    />
  )}
/>

and here’s a stub for addToCart:

const addToCart = ({ product, quantity }) => {
  console.log(product, quantity)
}

Verify that all is working (you should see a console log with the correct data you submitted when adding a product to the cart).

Now in the App component we add a new cart state property using hooks, which is initialized to an empty array:

const [cart, setCart] = useState([])

We use setCart() in the addToCart() method to update the cart when a product is entered. This time we check if we already have the product in the cart, and if so we update the quantity. Otherwise we just add the product as a new entry:

const addToCart = ({ product, quantity }) => {
  const index = cart.findIndex(
    itemInCart => itemInCart.product.slug === product.slug
  )

  let newCart = []

  if (index === -1) {
    //not existing
    newCart = [...cart, { product, quantity }]
  } else {
    quantity += cart[index].quantity
    newCart = cart
      .filter(item => item.product.slug !== product.slug)
      .concat({product, quantity})
  }

  setCart(newCart)
}

If the product is already existing in the cart, based on its slug, we increment its quantity. Otherwise, we add it as a new element.

This is plain JavaScript until the setCart() method call.

findIndex is an array method that returns the index of the first element in the array that satisfies the provided testing function.

You can check if all works fine by adding a console.log(newCart) at the end.

But let’s now visualize the cart, as we can’t expect customers to check out their order from the DevTools, right?

We need a Cart component that should appear only if there is something in the cart. I decide to put it on every page, just below the navigation links.

So we create a src/components/Cart.js file, and we include it in the src/App.js component, passing it the cart prop:

import Cart from './components/Cart.js'
//...

const App = () => {
  //...
  return (
    <Router>
      <div id="app">
        <aside>
          <Link to={`/`}>Products</Link>
          <Link to={`/add-product`}>Add product</Link>
        </aside>

        <main>
          <Cart cart={cart}/>
          //...
        </main>
      </div>
    </Router>
  )
}

Let’s dive into the Cart component code now.

We want to only show it when there is something in the cart:

import React from 'react'

export default props => {
  if (!props.cart.length) return ''

  return (
    <table className="cart">
    </table>
  )
}

then we create the table structure, and we iterate over the cart content, to show the details:

import React from 'react'

export default props => {
  if (!props.cart.length) return ''

  return (
    <table className="cart">
      <tbody>
        <tr>
          <td colSpan="3">
            <h2>Cart</h2>
          </td>
        </tr>
        <tr>
          <th>Item</th>
          <th>Quantity</th>
          <th>Total Price</th>
        </tr>

        {props.cart.map((value, index) => {
          return (
            <tr key={index}>
              <td>{value.product.name}</td>
              <td>{value.quantity}</td>
              <td>${value.quantity * value.product.price}</td>
            </tr>
          )
        })}
      </tbody>
    </table>
  )
}

and we add a little styling to src/App.css to make it look good:

table.cart tr,
table.cart th,
table.cart td {
  border: 1px solid #333;
  padding: 10px;
}
table.cart {
  margin: 0 auto;
}

table.cart button {
  padding: 10px 40px;
  background-color: lightgreen;
  border-color: gray;
}


Go to the next lesson