So I'm building an ecommerce app, and I have two values stored in the redux state to handle the cart. One is an array of objects, each of which contains two keys for objectID and quantity. The other is an object containing product information with their objectID as a key. Here's the code for my action/dispatch.
addToCart: (product) => {
return (dispatch, getState) => {
const {productQuantity, storeSelected, products, storeKeys} = getState().Ecommerce;
const UPC = product.UPC;
let newQuant = 1;
let newProducts;
for(var p in productQuantity) {
if (productQuantity[p].UPC === UPC) {
newProducts = products
newQuant += productQuantity[p].quantity;
productQuantity[p] = ({UPC, quantity: newQuant});
}
}
if (!newProducts) {
console.log("not found")
productQuantity.push({UPC, quantity: 1});
newProducts = {
...products,
[UPC]: {
...product,
price: product.stores[storeKeys[storeSelected]].price,
fromStore: storeKeys[storeSelected],
}
}
}
dispatch({
type: actions.CHANGE_CART,
products: newProducts,
productQuantity
});
};
},
The action definitely runs. Next up is my reducer.
case actions.CHANGE_CART:
console.log('This runs')
console.log(action.products);
console.log(action.productQuantity);
return {
...state,
products: action.products,
productQuantity: action.productQuantity
};
That also executes, and indeed when inspecting the state, productQuantity is updated in state but products is not. I've tried every configuration of definitions at this point and am tearing my hair out. Help would be much appreciated.
What I can confirm:
The first issue is that you're mutating the existing state before dispatching the action, and mutating state is forbidden in Redux:
const {productQuantity, storeSelected, products, storeKeys} = getState().Ecommerce;
const UPC = product.UPC;
let newQuant = 1;
let newProducts;
for(var p in productQuantity) {
if (productQuantity[p].UPC === UPC) {
newProducts = products
newQuant += productQuantity[p].quantity;
productQuantity[p] = ({UPC, quantity: newQuant});
}
}
Any changes to productQuantity
here are mutating the existing data in the Redux store. Don't do that.
I'm not quite clear on what's going on with products
here, because the logic feels a bit confusing.
My suggestions:
First, start using our new official Redux Toolkit package. It has a configureStore()
function that automatically sets up detection for accidental mutations, and will throw errors if you mutate.
Second, try to move all this modification logic into your reducer, instead of doing it on the action creation side.
Third, use Redux Toolkit's createSlice
API, which uses the Immer library to to allow you to write "mutating" logic in your reducers and safely converts it into correct immutably-updated results.