Search code examples
reactjsreduxredux-toolkit

Removing selected value from an array using redux toolkit MERN


commerce application made using MERN stack, in this application im handling state using redux toolkit and persist (im new to redux/redux toolkit). I have a problem in my cartReducers. so i wanna delete certain item in my cart but im having trouble to finish the task. The trouble is when im running cartReducers my data in cart state does not deleted i tried using filter, mutate the array but it doesn't work. I need help and explanation regarding my problem. Thanks

The problem is in cart page

cartRedux :

import { createSlice } from '@reduxjs/toolkit'


const cartSlice = createSlice({
    name: "cart",
    initialState: {
        products: [],
        quantity: 0,
        total: 0,
    },
    reducers: {
        addProduct: (state, action) => {
            state.quantity +=1;
            state.products.push(action.payload);
            state.total += action.payload.price * action.payload.quantity;
        },
        removeItem: (state, action) => {
            state.products.splice(state.products.findIndex((arrow) => arrow.id === action.payload), 1);
        }
    }
})

export const { addProduct, removeItem } = cartSlice.actions
export default cartSlice.reducer;

store :

import { configureStore, combineReducers } from "@reduxjs/toolkit"
import cartReducer from './cartRedux'
import userReducer from './userRedux'
import {
    persistStore,
    persistReducer,
    FLUSH,
    REHYDRATE,
    PAUSE,
    PERSIST,
    PURGE,
    REGISTER,
  } from "redux-persist";
import storage from "redux-persist/lib/storage"

const persistConfig = {
  key: "root",
  version: 1,
  storage,
};

const rootReducer = combineReducers({ user: userReducer, cart: cartReducer });

const persistedReducer = persistReducer(persistConfig, rootReducer);
  
export const store = configureStore({
    reducer: persistedReducer,
    middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
      },
    }),
})

export let persistor = persistStore(store);

Api Call :

export const login = async (dispatch, user) => {
    dispatch(loginStart());
    try {
        const res = await publicRequest.post("/login", user);
        dispatch(loginSuccess(res.data));
    } catch (err) {
        dispatch(loginFailure());
    }
}

export const addToCart = async (dispatch, product, quantity, user) => {
    try {
        const res = await userMethod.post("/cart/create", {
            userId: product.user,
            products: product
        });
        dispatch(addProduct({ ...product }));
        console.log(product)
    } catch (err) {
        console.log(err);
    }
}

export const removeCartItem = async (dispatch, cartId, itemId) => {
    try {
        const res = await userMethod.delete(`/cart/delete/${cartId}`);
       dispatch(removeItem(itemId));
       console.log(itemId)
    } catch (err) {
        console.log(err);
    }
}

Cart Page :

const Cart = () => {

    const cart = useSelector(state=>state.cart);
   
    const [ stripeToken, setStripeToken ] = useState(null);
    const navigate = useNavigate();
    
    const onToken = (token) => {
        setStripeToken(token);
    }

    const dispatch = useDispatch();
    const user = useSelector((state) => state.user.currentUser._id);

    
    const [ cartProducts, setCartProducts ] = useState([]);

    useEffect(() => {
        const makeRequest = async () => {
            try{
                const response = await userMethod.post("/test", {
                    tokenId: stripeToken.id,
                    amount: 500,
                })
                navigate("/success", { state: {stripeData: response.data, products: cart} });
            } catch (err) {
            } 
        }
        stripeToken &&  makeRequest();
    }, [stripeToken, cart.total, navigate])

    useEffect(() => {
        const makeCartRequest = async () => {
            try{
                const response = await userMethod.get(`/test${user}`)
                setCartProducts(response.data);
            } catch (err) {
                console.log(err);
            } 
        }
        makeCartRequest();
    }, [user?._id])

    
    const deleteCart = (cartId, itemId) => {
        removeCartItem(dispatch, cartId, itemId);
        window.location.reload();
    }



  return (
    <Container>
        <Navbar />
        <Wrapper>
            <Title>Your Bag</Title>
            <Top>
                <TopButton onClick={deleteCart}>Continue Shopping</TopButton>
                <TopTexts>
                    <TopText>Shooping Bag({cart.quantity})</TopText>
                    <TopText>Your Wishlist</TopText>
                </TopTexts>
                <TopButton type="filled">Checkout Now</TopButton>
            </Top>
            <Bottom>
                <Info>
                {cartProducts.map((product) => (
                    <Product>
                        <ProductDetail>
                            <Image src={product.products[0].img} alt="google"></Image>
                            <Details>
                                <ProductName><b>Product:</b> {product.products[0].title}</ProductName>
                                <ProductId><b>ID:</b> {product.products[0]._id}</ProductId>
                                <Button onClick={() => {
                                    deleteCart(product._id, product.products[0]._id)
                                }}>Delete</Button>
                            </Details>
                        </ProductDetail>
                        <PriceDetail>
                            <ProductAmountContainer>
                                <AiOutlineMinus />
                                <ProductAmount>{product.products[0].quantity}</ProductAmount>
                                <GrAdd />
                            </ProductAmountContainer>
                            <ProductPrice>$ {product.products[0].price * product.products[0].quantity}</ProductPrice>
                        </PriceDetail>
                    </Product>
                        ))}
                    <Hr></Hr>
                </Info>
                <Summary>
                    <SummaryTitle>Order Summary</SummaryTitle>
                        <SummaryItem>
                            <SummaryItemText>Subtotal</SummaryItemText>
                            <SummaryItemPrice>$ {cart.total}</SummaryItemPrice>
                        </SummaryItem>
                        <SummaryItem>
                            <SummaryItemText>Estimated Shipping</SummaryItemText>
                            <SummaryItemPrice>$ 5</SummaryItemPrice>
                        </SummaryItem>
                        <SummaryItem>
                            <SummaryItemText>Shipping Discount</SummaryItemText>
                            <SummaryItemPrice>$ -6</SummaryItemPrice>
                        </SummaryItem>
                        <SummaryItem>
                            <SummaryItemText type="total">Total</SummaryItemText>
                            <SummaryItemPrice>$ {cart.total}</SummaryItemPrice>
                        </SummaryItem>
                        <StripeCheckout 
                            name="Kimia shop" 
                            image="https://d3o2e4jr3mxnm3.cloudfront.net/Mens-Jake-Guitar-Vintage-Crusher-Tee_68382_1_lg.png"
                            billingAddress
                            shippingAddress
                            description={`your total is $ ${cart.total}`}
                            amount={cart.total * 100}
                            token={onToken}
                            stripeKey={KEY}
                            >
                            <Button>Checkout Now</Button>
                        </StripeCheckout>
                </Summary>
            </Bottom>
        </Wrapper>
        <Footer />
    </Container>
  )
}

export default Cart

Solution

  • From what I can tell you aren't actually dispatching any action to the store to remove/delete any item. The removeCartItem action still needs to be dispatched to the store in the deleteCart handler.

    const dispatch = useDispatch();
    
    ...
    
    const deleteCart = (cartId, itemId) => {
      dispatch(removeCartItem(dispatch, cartId, itemId));
    };
    

    You also are using redux-toolkit but appear to still write "old redux" code. removeCartItem is a "Thunk"-ish function, so use createAsyncThunk and create a real Thunk.

    Example:

    import { createAsyncThunk } from '@reduxjs/toolkit';
    
    export const removeCartItem = createAsyncThunk(
      "cart/removeItem",
      async (cartId, thunkAPI) => {
        try {
          const res = await userMethod.delete(`/cart/delete/${cartId}`);
        } catch (error) {
          console.log(error);
          return thunkAPI.rejectWithValue(error);
        }
      }
    );
    
    const initialState = {
      products: [],
    };
    
    const cartSlice = createSlice({
      name: "cart",
      initialState,
      reducers: {
        addProduct: (state, action) => {
          state.products.push(action.payload);
        },
        removeItem: (state, action) => {
          const index = state.products.findIndex((item) => item._id === action.payload);
          if (index !== -1) {
            state.products.splice(index, 1);
          }
        }
      }
    });
    
    export cartProductsSelector = state => state.cart.products;
    export cartQuantitySelector = state => state.cart.products.length;
    export cartTotalSelector = state => state.cart.products.reduce(
      (total, { price = 0, quantity = 1 }) => total + quantity * price,
      0,
    );
    

    Notice that "total" and "quantity" were removed from the cart state slice since they are derived "state", and selector functions are exported to replace them.

    const Cart = () => {
      const cartProducts = useSelector(cartProductsSelector);
      const cartQuantity = useSelector(cartQuantitySelector);
      const cartTotal = useSelector(cartTotalSelector);
    
      ...
    
      const deleteCart = async (cartId, itemId) => {
        try {
          // Asynchronous call to backend, wait to resolve
          await dispatch(removeCartItem(cartId)).unwrap;
    
          // Now dispatch action to remove item from state
          dispatch(removeItem(itemId));
        } catch(error) {
          // handle any errors
        }
      };
    
      ...
    };