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
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
}
};
...
};