Home > Back-end >  React - How to handle this complex "add to cart" logic
React - How to handle this complex "add to cart" logic

Time:05-29

I am currently doing a "frontend only" e-commerce website for training, and I implemented a cart and some functions to make it work. This is my first react cart so I implemented it thanks to a tutorial. I said "frontend only" because I created an array that gathers the products available on the website, so it is like a fake database.

My products - paintings - have three different prices possible according to their size. I can get the right price after choosing a format when I click on the "Add to cart" button.

My issue is that I would like to add two same products in the cart, but with different sizes. I tried to pass the format prices as arguments in order to compare it to the already existing item in the cart, but it didn't work.

Here is the function onAdd in App.js:

const onAdd = (product, formatPrice) => {
const exists = cartItems.find((item) => item.id === product.id);
if (exists) {
  setCartItems(
    cartItems.map((item) =>
      item.id === product.id
        ? {
            ...exists,
            quantity: exists.quantity   1
          }
        : item
    )
  );
} else {
  setCartItems([
    ...cartItems,
    { ...product, quantity: 1, formatPrice: formatPrice },
  ]);
}

};

And the code for displaying the items informations in the cart:

<tbody>
          {cartItems.map((item) => {
            return (
              <tr key={item.id}>
                <td>
                  <Button
                    onClick={() => {
                      onDelete(item.id);
                    }}
                  >
                    <Delete />
                  </Button>
                </td>
                <td width="100px">
                  <Link to={"/productDetails/"   item.id}><img src={item.img} alt={item.name} /></Link>
                </td>
                <td><Link to={"/productDetails/"   item.id}>{item.name}</Link></td>
                <td>{parseFloat(item.formatPrice)}€</td>
                <td>
                  <div>
                    <Button variant="text" onClick={() => onRemove(item)}>
                      <Remove />
                    </Button>
                    <span>{item.quantity}</span>
                    <Button variant="text" onClick={() => onAdd(item)}>
                      <Add />
                    </Button>
                  </div>
                </td>
                <td>{parseFloat(item.formatPrice) * item.quantity}€</td>
              </tr>
            );
          })}
          <tr>
            <td colSpan={5}>Total</td>
            <td>
              {cartItems.reduce(
                (accumulator, product) =>
                  accumulator   product.quantity * product.formatPrice,
                0
              )}
              €
            </td>
          </tr>
        </tbody>

If you would like to see the whole project, here is the link: https://github.com/EstelleRoges/plant-painting-shop

I kind of know I have to add some logic in the if(exists) part, compare the format prices and add a new product if there is any match, but my attempts all failed or provoked an unexpected behavior.

Thank you in advance for your help.

CodePudding user response:

Try doing this:-

const onAdd = (product, formatPrice) => {
    const index = cartItems.findIndex((item) => item.id === product.id && item.formatPrice === formatPrice);
    if (index > -1) {
      let _cartItems = cartItems;
      _cartItems[index].quantity  = 1;
      setCartItems(_cartItems);
    } else {
      setCartItems([
        ...cartItems,
        { ...product, quantity: 1, formatPrice: formatPrice },
      ]);
    }
  };

CodePudding user response:

Firstly I'll give an answer. followed up with some advice for you:

So in your Cart.jsx you I can see that your passing in an array of objects, being products, as a prop however you've refer to it as items.CartItems rather than props.CartItems which is fine. However I would suggest maybe changing the array that you pass in to follow a pattern something like this

[{ id: 'the-product-id', variants: { small: 2, medium: 1 } }]

You could then import your ProductList in your Cart.jsx file and find the products in your .map to get the images and other information.

This should help you with adding product variants to the cart.

Lastly I just wanted to give you some advice on React. It looks like your falling into whats called 'prop drilling' and it will become a problem for you at some point and make your life difficult later down the line.

I would strongly suggest looking into the Context API and creating some Providers to handle the state of your cart etc... You can then create some custom hooks to tap into the Cart state. For example you could export a addToCart hook that will add to your Carts state. The same for deleteFromCart, etc...

Props are great, but a good rule to follow is that Props should only go one level deep, when you start passing props from components that have nothing to do with that prop it gets very confusing very fast. A good use of props could be a callback for an onClick function or changing the appearance of the component. Like you could have a 'filled' prop a a button that changes if the button has a filled background or not.

Please don't try and handle application logic using only props.

Also, use components as much as you can, if you have a 'Page' and find yourself writing lots of html out take a look at the code and as yourself 'what can I pull out of this file as a component'. That way if you ever want to change the look and feel of the website you do it in one place and it updates everywhere.

  • Related