DEV Community

Anthony Santonocito
Anthony Santonocito

Posted on

Build a React Hooks Shopping Cart with useState and useEffect

Video Walkthrough


subscribe on youtube for more content.

Article Explanation of Hooks

In this article, we will use hooks to create a video game shop and shopping cart.

First, I will show an example of how to use the useState hook.

import React, { useState } from "react";

const Shop = () => {
  const [open, setOpen] = useState(true);
  console.log(open)

  const closeStore = () => {
    setOpen(false)
}
  return(
    <div>
    <input type="submit" value="close" onClick={() => closeStore()} />
    </div>
)
}

export default Shop;
Enter fullscreen mode Exit fullscreen mode

In this example, open is a key that holds the useState argument as its value. useState(true), open = true.

setOpen is a function that takes a value as an argument.

setOpen will set open to the new value passed to setOpen.

setOpen(false), sets open = false

This shows a button that can be clicked in order to change the value of open from true to false.

Let's try a more complex use case.

In App.js we will return a div with our soon to be created Shop component:

import React from "react";
import Shop from "./shop/Shop";

function App() {
  return (
    <div>
      <Shop />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

We will create the shop component next. Create a folder in src named shop. Then create a file in that folder named Shop.js

The finished Shop.js code is at the bottom of the article.

We are going to return an empty functional component to get us started:

import React, { useState, useEffect } from "react";

const Shop = () => {
    return <div />
}

export default Shop;
Enter fullscreen mode Exit fullscreen mode

Let's add our inventory as an array labeled items:

const Shop = () => {
  const items = [
    {
      id: 1,
      name: "overwatch",
      price: 20,
    },
    {
      id: 2,
      name: "minecraft",
      price: 32,
    },
    {
      id: 3,
      name: "fortnite",
      price: 51,
    },
  ];
  return <div />
}
Enter fullscreen mode Exit fullscreen mode

We are only selling these three video games. We need to display them. So, we will create a new formatted array called listItems using .map(). Now, we should return listItems:

  const listItems = items.map((el) => (
    <div key={el.id}>
      {`${el.name}: $${el.price}`}
      <input type="submit" value="add" onClick={() => addToCart(el)} />
    </div>
  ));

return(<div>{listItems}</div>)
Enter fullscreen mode Exit fullscreen mode

Above items, we will create our first useState hook:

const [cart, setCart] = useState([]);
Enter fullscreen mode Exit fullscreen mode

The const cart is where we will hold our cart state. We can call setCart() and pass in the state changes we want to make to cart. Let's create our addToCart function using setCart:

  const addToCart = (el) => {
      setCart([...cart, el]);
  };
Enter fullscreen mode Exit fullscreen mode

addToCart takes the element selected and adds it to the cart array.

We are going to display the cart, in our app, under our store. First, make a new formatted array from the cart array:

  const cartItems = cart.map((el) => (
    <div key={el.id}>
      {`${el.name}: $${el.price}`}
      <input type="submit" value="remove" onClick={() => removeFromCart(el)} />
    </div>
  ));
Enter fullscreen mode Exit fullscreen mode

We can create our removeFromCart function using the filter method. note* We will make a copy of the cart state before filtering:

  const removeFromCart = (el) => {
    let hardCopy = [...cart];
    hardCopy = hardCopy.filter((cartItem) => cartItem.id !== el.id);
    setCart(hardCopy);
  };
Enter fullscreen mode Exit fullscreen mode

Change the return statement to include cartItems:

  return (
    <div>
      STORE
      <div>{listItems}</div>
      <div>CART</div>
      <div>{cartItems}</div>
    </div>
  );
Enter fullscreen mode Exit fullscreen mode

Finally, we will keep track of the total using useState and useEffect:

  const [cartTotal, setCartTotal] = useState(0);

  useEffect(() => {
    total();
  }, [cart]);

  const total = () => {
    let totalVal = 0;
    for (let i = 0; i < cart.length; i++) {
      totalVal += cart[i].price;
    }
    setCartTotal(totalVal);
  };
Enter fullscreen mode Exit fullscreen mode

The useEffect hook contains an arrow function. Inside the arrow function, we call our total function.

The second argument in useEffect is the dependency array containing [cart].

useEffect will detect changes in the variables named within its dependency array. When it detects a change, it will run again.

Every time an item is added or removed from the cart, useEffect will detect a change in cart and run the total function.

Finally, place total in your return:

import React, { useState, useEffect } from "react";

const Shop = () => {
  const [cart, setCart] = useState([]);
  const [cartTotal, setCartTotal] = useState(0);
  const items = [
    {
      id: 1,
      name: "overwatch",
      price: 20,
    },
    {
      id: 2,
      name: "minecraft",
      price: 32,
    },
    {
      id: 3,
      name: "fortnite",
      price: 51,
    },
  ];

  useEffect(() => {
    total();
  }, [cart]);

  const total = () => {
    let totalVal = 0;
    for (let i = 0; i < cart.length; i++) {
      totalVal += cart[i].price;
    }
    setCartTotal(totalVal);
  };

  const addToCart = (el) => {
      setCart([...cart, el]);
  };

  const removeFromCart = (el) => {
    let hardCopy = [...cart];
    hardCopy = hardCopy.filter((cartItem) => cartItem.id !== el.id);
    setCart(hardCopy);
  };

  const listItems = items.map((el) => (
    <div key={el.id}>
      {`${el.name}: $${el.price}`}
      <input type="submit" value="add" onClick={() => addToCart(el)} />
    </div>
  ));

  const cartItems = cart.map((el) => (
    <div key={el.id}>
      {`${el.name}: $${el.price}`}
      <input type="submit" value="remove" onClick={() => removeFromCart(el)} />
    </div>
  ));

  return (
    <div>
      STORE
      <div>{listItems}</div>
      <div>CART</div>
      <div>{cartItems}</div>
      <div>Total: ${cartTotal}</div>
    </div>
  );
};

export default Shop;
Enter fullscreen mode Exit fullscreen mode

Watch the video for more! Next, we will refactor this code to use Redux. After that, we will start making http requests using the fetch api and redux-thunk. Finally, we will convert the project to redux-saga. The videos are up on youtube already Youtube Channel Link and the articles are on their way!

Top comments (4)

Collapse
 
tiagojpdias profile image
Tiago Dias

Some quick notes:

1) You don't need useEffect nor useState to grab the total value in the cart. You can grab that value by using the cart already in state.

2) Anytime updating state which depends on the previous state its a good practice to use functional updates.

Refactored it a bit to address both points mentioned before:

import React from "react";

const items = [
  {
    id: 1,
    name: "overwatch",
    price: 20,
  },
  {
    id: 2,
    name: "minecraft",
    price: 32,
  },
  {
    id: 3,
    name: "fortnite",
    price: 51,
  },
];

const Shop = () => {
  const [cart, setCart] = React.useState([]);
  const cartTotal = cart.reduce((total, { price = 0 }) => total + price, 0);

  const addToCart = (item) => setCart((currentCart) => [...currentCart, item]);

  const removeFromCart = (item) => {
    setCart((currentCart) => {
      const indexOfItemToRemove = currentCart.findIndex((cartItem) => cartItem.id === item.id);

      if (indexOfItemToRemove === -1) {
        return currentCart;
      }

      return [
        ...currentCart.slice(0, indexOfItemToRemove),
        ...currentCart.slice(indexOfItemToRemove + 1),
      ];
    });
  };

  const amountOfItems = (id) => cart.filter((item) => item.id === id).length;

  const listItemsToBuy = () => items.map((item) => (
    <div key={item.id}>
      {`${item.name}: $${item.price}`}
      <button type="submit" onClick={() => addToCart(item)}>Add</button>
    </div>
  ));

  const listItemsInCart = () => items.map((item) => (
    <div key={item.id}>
      ({amountOfItems(item.id)} x ${item.price}) {`${item.name}`}
      <button type="submit" onClick={() => removeFromCart(item)}>Remove</button>
    </div>
  ));

  return (
    <div>
      STORE
      <div>{listItemsToBuy()}</div>
      <div>CART</div>
      <div>{listItemsInCart()}</div>
      <div>Total: ${cartTotal}</div>
      <div>
        <button onClick={() => setCart([])}>Clear</button>
      </div>
    </div>
  );
};

export default Shop;

Enter fullscreen mode Exit fullscreen mode
Collapse
 
papasanto profile image
Anthony Santonocito

I agree. This is how Iโ€™d do it in practice. I wanted to explain useEffect in the article so I created a use case where I could use it as well as itโ€™s dependency array. Iโ€™ll heart this so people can see this as well.

Collapse
 
suraj246 profile image
Suraj246

how to stop adding same product in cart again and again using useState

Collapse
 
amaben2020 profile image
amaben2020

Nice tutorial, easy for beginners to understand useState and useEffect, Tiagos implementation is superb but may confuse beginners.