Reputation: 654
Whenever I click add to cart button, the action is fired but redux state is not been updated (the initial state is not changing but the action is triggered).
const CartScreen = () => {
const { id } = useParams();
const { search } = useLocation();
const [searchParms] = useSearchParams();
const productId = id;
const qty = search ? Number(search.split("=")[1]) : 1;
const dispatch = useDispatch()
useEffect(() => {
if (productId){
dispatch(addToCart(productId, qty))
}
}, [dispatch, productId, qty])
return (
<div>
<h1>Add to CART</h1>
</div>
);
};
export default CartScreen
Cart action
export const addToCart = (id, qty) => async (dispatch, getState) =>{
const {data} = await axios.get(`http://127.0.0.1:8000/api/products/${id}`)
dispatch({
type: CART_ADD_ITEM,
payload:{
product:data._id,
name:data.name,
image:data.image,
countInStock:data.countInStock,
qty
}
})
localStorage.setItem('cartItems', JSON.stringify(getState().cart.cartItems))
}
Cart Reducer
export const cartReducer = (state = { cartItems: []}, action) =>{
switch(action.type){
case CART_ADD_ITEM:
const item = action.payload
const existItem = state.cartItems.findIndex(x => x.product === item.product)
if (existItem){
return{
...state,
cartItems: state.cartItems.map(x =>
x.product === existItem.product ? item : x)
}
} else{
return{
...state,
cartItems:[...state.cartItems, item]
}
}
default:
return state
}
}
Redux store
const reducer = combineReducers({
productList: productListReducer,
productDetails: productDetailsReducer,
cart: cartReducer,
})
const initialState = {
cart:{cartItems:cartItemsFromStorage}
};
const middleware = [thunk];
const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
From redux dev tools I can see that the action I triggered. The item is getting to cart reducer because when I console.log item
in const item=action.payload
from the cartReducer, I get the particular item in Browser console, yet the cartItem redux state remains at the initial value, it's not updated
Upvotes: 0
Views: 1313
Reputation: 202605
The issue is that you are searching for an existing product and returning the found index (array.findIndex
) then using the index value as a boolean condition (if (existItem) {...}
).
This won't work as you are expecting since all non-zero numbers are truthy, while 0
is falsey. This means if no cart item products match that -1
is returned and the logic will treat this as an existing item. This is compounded later when updating the cart via array.map
... if existItem
is -1
this means there is no matching product, the new state.cartItems
will be a new array, but it will not contain the new item
object. In other words it will be just a copy of the previous state.
cartItems
starts initially as an empty array, so existItem
will always return -1
when first adding an item to the cart.
An additional unintentional bug occurs when a product does exist in the cartItems
array and it's the 0th element. existItem
will equal 0
and is thus falsey and item
will be added to cartItems
array as a duplicate.
@Chigbogu is correct regarding the use of array.findIndex
and array.find
, though I'd recommend using array.some
if you are just checking the the cart items array has the item or not. This indicates you are working with a boolean explicitly. Rename existItem
to hasItem
or similar so the name also indicates a boolean value (this is by convention).
export const cartReducer = (state = { cartItems: []}, action) =>{
switch(action.type) {
case CART_ADD_ITEM: {
const newItem = action.payload;
const hasItem = state.cartItems.some(item => item.product === newItem.product);
if (hasItem) {
return {
...state,
cartItems: state.cartItems.map(item =>
item.product === newItem.product ? newItem : item
)
}
} else {
return {
...state,
cartItems: [...state.cartItems, newItem]
}
}
}
default:
return state;
}
};
Upvotes: 0
Reputation: 24
Array.prototype.find()- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
Array.prototype.findIndex()- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
Using Array.prototype.findIndex() will basically look for and return the index of the first found item, and -1
if not found. While Array.prototype.find() returns the first element in the array that matched the criteria provided.
export const cartReducer = (state = { cartItems: [] }, action) => {
switch(action.type){
case CART_ADD_ITEM:
const item = action.payload;
// use Array.prototype.find() instead
// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
const existItem = state.cartItems.find(x => x.product === item.product);
if (existItem){
return{
...state,
cartItems: state.cartItems.map(x =>
x.product === existItem.product ? item : x)
};
} else{
return{
...state,
cartItems: [...state.cartItems, item]
};
}
default:
return state;
}
};
Upvotes: 1