Right now I can reflect changes of total price if quantity changes. here is an example what I am doing now:
price of every single item
quantity {2} {200}
quantity {2} {100}
total: price = 600
Here is my cart slice:
const cartSlice = createSlice({
name: 'cart',
initialState: {
items: [],
total: 0,
},
reducers: {
addItem(state, action) {
const { id, title, price, size, color, image, quantity = 1 } = action.payload;
const existingItem = state.items.find(item => item.id === id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
state.items.push({ id, title, price, size, color, image, quantity });
}
state.total += price * quantity;
},
removeItem(state, action) {
const { id, quantity = state.quantity } = action.payload;
const existingItem = state.items.find(item => item.id === id);
if (existingItem) {
if (existingItem.quantity > quantity) {
existingItem.quantity -= quantity;
} else {
state.items = state.items.filter(item => item.id !== id);
}
state.total -= existingItem.price * quantity;
}
},
removeSingleItem(state, action) {
const { id, quantity = 1 } = action.payload;
const existingItem = state.items.find(item => item.id === id);
if (existingItem) {
if (existingItem.quantity > quantity) {
existingItem.quantity -= quantity;
} else {
state.items = state.items.filter(item => item.id !== id);
}
state.total -= existingItem.price * quantity;
}
},
},
});
export const {
addItem,
removeItem,
addSingleItem,
removeSingleItem
} = cartSlice.actions;
export default cartSlice.reducer;
My expected result will be:
price of every single item
quantity {2} {400}
quantity {2} {200}
total: price = 600
React state should be the minimum data it takes to represent the "state"; it's considered a bit of a React anti-pattern to store derived state in state. In this case a cart total can be computed directly from the state.cart.items
array.
Example:
const items = useSelector(state => state.cart.items);
const total = items.reduce((total, { price = 0, quantity = 0 }) => {
return total + price * quantity;
}, 0);
If you want or need to you can memoize the total using the useMemo
hook.
const items = useSelector(state => state.cart.items);
const total = useMemo(() => {
return items.reduce((total, { price = 0, quantity = 0 }) => {
return total + price * quantity;
}, 0);
}, [items]);
redux-toolkit
also exports reselect
functions. You could use createSelector
to create a memoized selector function. In the cartSlice
file create an input selector to select the state.cart.items
array and then create the memoized selector`.
import { createSlice, createSelector } from '@reduxjs/toolkit';
const cartSlice = createSlice({
...
});
export const cartItemsSelector = state => state.cart.items;
export const cartTotalSelector = createSelector(
[cartItemsSelector],
items => items.reduce((total, { price = 0, quantity = 0 }) => {
return total + price * quantity;
}, 0)
);
const items = useSelector(cartItemsSelector);
const total = useSelector(cartTotalSelector);
...
Remove the total
state and logic from the cart slice.
const cartSlice = createSlice({
name: 'cart',
initialState: {
items: [],
},
reducers: {
addItem(state, action) {
const { id } = action.payload;
const existingItem = state.items.find(item => item.id === id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
state.items.push({ ...action.payload, quantity: 1 });
}
},
removeItem(state, action) {
const { id, quantity = 1 } = action.payload;
const existingItem = state.items.find(item => item.id === id);
if (existingItem) {
if (existingItem.quantity > quantity) {
existingItem.quantity -= quantity;
} else {
state.items = state.items.filter(item => item.id !== id);
}
}
},
removeSingleItem(state, action) {
const { id, quantity = 1 } = action.payload;
const existingItem = state.items.find(item => item.id === id);
if (existingItem) {
if (existingItem.quantity > quantity) {
existingItem.quantity -= quantity;
} else {
state.items = state.items.filter(item => item.id !== id);
}
}
},
},
});
export const {
addItem,
removeItem,
addSingleItem,
removeSingleItem
} = cartSlice.actions;
export default cartSlice.reducer;