skip to Main Content

I am trying to get total price of items added and removed in Cart, but total just increase and no decrease when remove one item.

I’m working with reducer and I am almost sure that I’ve implemented here the logic but I’m very stuck in this trouble.

This is my code reducer in CartRedux.js

const cartSlice = createSlice({
  name: "cart",
  initialState: {
    products: [],
    quantity: 0,
    total: 0,
  },
  reducers: {
    addProduct: (state, action) => {
      state.quantity += 1;
      state.products.push((product) => product._id === action.payload);
      state.total += action.payload.price * action.payload.quantity;
    },
    clearCart: (state) => {
      state.products = [];
    },
    deleteProductStart: (state) => {
      state.isFetching = true;
      state.error = false;
    },
    deleteProductSuccess: (state, action) => {
      state.isFetching = false;
      state.quantity -= 1;
      state.products.splice(
        state.products.findIndex((item) => item._id === action.payload),
        1
      );
      //agregar aquí la función para restar el total en el RESUMEN DE COMPRA
    },
    deleteProductFailure: (state) => {
      state.isFetching = false;
      state.error = true;
    },
  }
});

3

Answers


  1. You can use the array filter method to remove a specific element from an array without mutating the original state.

    state.products = state.products.filter(element => element._id !== action.payload);
    

    Alternatively you can use slice to remove elements from your state:

    state.products = [
        ...state.products.slice(0, action.payload),
        ...state.products.slice(action.payload + 1)
    ],
    

    Also you are not decreasing your total value when you delete one item from your state.

    state.total -= action.payload.price * action.payload.quantity;
    

    All in all your deleteProductSuccess function should look like:

    deleteProductSuccess: (state, action) => {
          state.isFetching = false;
          state.quantity -= 1;
          state.products = state.products.filter(element => element._id !== action.payload);
          state.total -= action.payload.price * action.payload.quantity;
    },
    
    Login or Signup to reply.
  2. The deleteProductSuccess case reducer isn’t computing/recomputing a total, but instead is only adjusting the total quantity.

    Both quantity and total are what would be considered derived "state", meaning these are easily computed values from actual state, and don’t generally belong as part of the state as it’s considered a React anti-pattern. The suggestion here is to instead store just the products array and compute the derived state the cart.products array.

    Example:

    const cartSlice = createSlice({
      name: "cart",
      initialState: {
        products: [],
      },
      reducers: {
        addProduct: (state, action) => {
          const item = state.products.find((product) => product._id === action.payload.id);
    
          if (item) {
            item.quantity += action.payload.quantity;
          } else {
            state.products.push(action.payload);
          }
        },
        clearCart: (state) => {
          state.products = [];
        },
        deleteProductStart: (state) => {
          state.isFetching = true;
          state.error = false;
        },
        deleteProductSuccess: (state, action) => {
          state.isFetching = false;
          state.products.splice(
            state.products.findIndex((item) => item._id === action.payload.id),
            1
          );
        },
        deleteProductFailure: (state) => {
          state.isFetching = false;
          state.error = true;
        },
      }
    });
    
    // Note: the selector functions need to access the correct state from root.
    // This is dictated by how you combine reducers and structure the reducer tree.
    // This is just an example since I've no idea what the overall state
    // looks like, it assumes `cart` is a root reducer/state property.
    export const cartQuantitySelector = (state) => state.cart.products.reduce(
      (quantity, item) => quantity + item.quantity,
      0
    );
    export const cartTotalSelector = (state) => state.cart.products.reduce(
      (total, item) => total + item.price * item.quantity,
      0
    );
    

    Getting the cart quantity & cart total in component

    import { useSelector } from 'react-redux';
    import { cartQuantitySelector, cartTotalSelector } from '../path/to/CartRedux';
    
    ...
    
    const cartQuantity = useSelector(cartQuantitySelector);
    const cartTotal = useSelector(cartTotalSelector);
    ...
    
    Login or Signup to reply.
  3. I am sharing the code , as I did the same for mini project

    store.js

            import { configureStore } from "@reduxjs/toolkit";
            import cartReducer from "../features/cartSlice";
            
            export const store = configureStore({
              reducer: {
                allCart: cartReducer,
              },
            });
        
        
            Navbar.js
            import React, { useEffect } from "react";
            import {
              MDBContainer,
              MDBNavbar,
              MDBNavbarBrand,
              MDBBtn,
            } from "mdb-react-ui-kit";
            import { Link } from "react-router-dom";
            import { useDispatch, useSelector } from "react-redux";
            
            import { getCartTotal } from "../features/cartSlice";
            
            export default function App() {
              
              const { cart, totalQuantity } = useSelector((state) => state.allCart);
              const dispatch = useDispatch();
            
              useEffect(() => {
                dispatch(getCartTotal());
              }, [cart]);
            
              return (
                <MDBNavbar light bgColor="light">
                  <MDBContainer fluid>
                    <MDBNavbarBrand>Navbar</MDBNavbarBrand>
                    <span>
                      <Link to="/">All Product </Link>
                    </span>
                    <MDBBtn color="light">
                      <Link to="/cart">Cart({totalQuantity})</Link>
                    </MDBBtn>
                  </MDBContainer>
                </MDBNavbar>
              );
            }
    
        cartSlice.js
        import { createSlice } from "@reduxjs/toolkit";
        import productData from "../productData";
        
        const initialState = {
          cart: [],
          items: productData,
          totalQuantity: 0,
          totalPrice: 0,
        };
        
        const cartSlice = createSlice({
          name: "cart",
          initialState,
          reducers: {
            addToCart: (state, action) => {
              let find = state.cart.findIndex((item) => item.id === action.payload.id);
              if (find >= 0) {
                state.cart[find].quantity += 1;
              } else {
                state.cart.push(action.payload);
              }
            },
        
            getCartTotal: (state) => {
              let { totalQuantity, totalPrice } = state.cart.reduce((cartTotal, cartItem) => {
                  const { price, quantity } = cartItem;
                  const itemTotal = price * quantity;
                  cartTotal.totalPrice += itemTotal;
                  cartTotal.totalQuantity += quantity;
                  return cartTotal;
                },
                {
                  totalPrice: 0,
                  totalQuantity: 0,
                }
              );
              state.totalPrice = parseInt(totalPrice.toFixed(2));
              state.totalQuantity = totalQuantity;
            },
        
            removeItem: (state, action) => {
              state.cart = state.cart.filter((item) => item.id !== action.payload);
            },
        
            increaseItemQuantity: (state, action) => {
              state.cart = state.cart.map((item) => {
                if (item.id === action.payload) {
                  return { ...item, quantity: item.quantity + 1 };
                }
                return item;
              });
            },
            decreaseItemQuantity: (state, action) => {
              state.cart = state.cart.map((item) => {
                if (item.id === action.payload) {
                  return { ...item, quantity: item.quantity - 1 };
                }
                return item;
              });
            },
          },
        });
        
        export const {
          addToCart,
          getCartTotal,
          removeItem,
          increaseItemQuantity,
          decreaseItemQuantity,
        } = cartSlice.actions;
        
        export default cartSlice.reducer;
    
        cartPage.js
        import { useEffect } from "react";
        import { useSelector, useDispatch } from "react-redux";
        
        import {
          getCartTotal,
          removeItem,
          decreaseItemQuantity,
          increaseItemQuantity,
        } from "../features/cartSlice";
        
        const CartPage = () => {
          const { cart, totalQuantity, totalPrice } = useSelector((state) => state.allCart );
        
          const dispatch = useDispatch();
        
          useEffect(() => {
            dispatch(getCartTotal());
          }, [cart]);
        
          return (
            <div>
              <section className="h-100 gradient-custom">
                <div className="container py-5">
                  <div className="row d-flex justify-content-center my-4">
                    <div className="col-md-8">
                      <div className="card mb-4">
                        <div className="card-header py-3">
                          <h5 className="mb-0">Cart - {cart.length} items</h5>
                        </div>
                        <div className="card-body">
                          {cart?.map((data) => (
                            <div className="row">
                              <div className="col-lg-3 col-md-12 mb-4 mb-lg-0">
                                <div
                                  className="bg-image hover-overlay hover-zoom ripple rounded"
                                  data-mdb-ripple-color="light"
                                >
                                  <img
                                    src={data.img}
                                    className="w-100"
                                    alt="Blue Jeans Jacket"
                                  />
                                </div>
                              </div>
        
                              <div className="col-lg-5 col-md-6 mb-4 mb-lg-0">
                                <p>
                                  <strong>{data.title}</strong>
                                </p>
        
                                <button
                                  type="button"
                                  className="btn btn-primary btn-sm me-1 mb-2"
                                  data-mdb-toggle="tooltip"
                                  title="Remove item"
                                  onClick={() => dispatch(removeItem(data.id))}
                                >
                                  <i className="fas fa-trash"></i>
                                </button>
                              </div>
        
                              <div className="col-lg-4 col-md-6 mb-4 mb-lg-0">
                                <div
                                  className="d-flex mb-4"
                                  style={{ maxWidth: "300px" }}
                                >
                                  <button
                                    className="btn btn-primary px-3 me-2"
                                    onClick={() =>
                                      dispatch(decreaseItemQuantity(data.id))
                                    }
                                  >
                                    <i className="fas fa-minus"></i>
                                  </button>
        
                                  <div className="form-outline">
                                    <input
                                      id="form1"
                                      min="0"
                                      name="quantity"
                                      value={data.quantity}
                                      type="number"
                                      className="form-control"
                                      onChange={() => null}
                                    />
                                    <label className="form-label" for="form1">
                                      Quantity
                                    </label>
                                  </div>
        
                                  <button
                                    className="btn btn-primary px-3 ms-2"
                                    onClick={() =>
                                      dispatch(increaseItemQuantity(data.id))
                                    }
                                  >
                                    <i className="fas fa-plus"></i>
                                  </button>
                                </div>
        
                                <p className="text-start text-md-center">
                                  <strong>{data.price}</strong>
                                </p>
                              </div>
                              <hr className="my-4" />
                            </div>
                          ))}
                        </div>
                      </div>
                    </div>
                    <div className="col-md-4">
                      <div className="card mb-4">
                        <div className="card-header py-3">
                          <h5 className="mb-0">Summary</h5>
                        </div>
                        <div className="card-body">
                          <ul className="list-group list-group-flush">
                            <li className="list-group-item d-flex justify-content-between align-items-center border-0 px-0 pb-0">
                              Total Quantity
                              <span>{totalQuantity}</span>
                            </li>
        
                            <li className="list-group-item d-flex justify-content-between align-items-center border-0 px-0 mb-3">
                              <div>
                                <strong>Total amount</strong>
                              </div>
                              <span>
                                <strong>{totalPrice}</strong>
                              </span>
                            </li>
                          </ul>
        
                          <button
                            type="button"
                            className="btn btn-primary btn-lg btn-block"
                          >
                            Go to checkout
                          </button>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </section>
            </div>
          );
        };
        
        export default CartPage;
    
        ProductCard.js
        import React from "react";
        import {
          MDBCard,
          MDBCardBody,
          MDBCardTitle,
          MDBCardText,
          MDBCardImage,
          MDBBtn,
          MDBContainer,
          MDBRow,
          MDBCol,
        } from "mdb-react-ui-kit";
        import { useSelector, useDispatch } from "react-redux";
        import { addToCart } from "../features/cartSlice";
        
        export default function App() {
        
          const items = useSelector((state) => state.allCart.items);
          
          const dispatch = useDispatch();
        
          return (
            <div className="m-2">
              <MDBContainer>
                <MDBRow className="mb-3">
                  {items.map((item) => (
                    <MDBCol key={item.id} size="md">
                      <MDBCard>
                        <MDBCardImage src={item.img} position="top" alt="..." />
                        <MDBCardBody>
                          <MDBCardTitle>{item.title}</MDBCardTitle>
                          <MDBCardText>{item.price}</MDBCardText>
                          <MDBBtn onClick={() => dispatch(addToCart(item))}>
                            Add to Cart
                          </MDBBtn>
                        </MDBCardBody>
                      </MDBCard>
                    </MDBCol>
                  ))}
                </MDBRow>
              </MDBContainer>
            </div>
          );
        }
    

    I hope it will help you

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search