How to update Global state correctly? - reactjs

I'm working on an e-comerce type application. I was implementing the cart functionality but I got stuck and can't seem to find a way.
Basically I've a global state called cart. which looks like this:
cart: [
{
book: {},
quantity: 1
}
]
I want to update the quantity when a book is found in it.
case "ADD_TO_CART":
let book_index = state.cart.findIndex(
(el) => el.book._id === action.payload._id
);
let newCart;
if (book_index === -1){
newCart = state.cart.concat({book: action.payload, quantity: 1})
} else {
newCart = [...state.cart];
newCart[book_index].quantity = newCart[book_index].quantity + 1;
}
return {
...state,
cart: newCart,
};
The problem in it is that when a book is found, it updates the quantity by a factor of 2. So the quantity goes like 1, 3, 5, 7 so on.
I tried to replace 1 by 0.5 to see if it's doubling it and it's not.

Related

cant update quantity of a product in a shopping cart using react-redux

I am making a small shopping cart, which you can add products to it,
every product is of this format:
{
id: 1,
img: "",
title: "",
desc: "",
category: "",
price: 12,
quantity: 0,
}
id, price, and quantity are integers.
In my shopping cart, I have only 2 actions defined, which you can add or remove to the shopping cart.
if the item is already there you have to increase the quantity, calculate the total price and total items in the cart.
I am using react-redux for this.
My reducer looks like this:
case ADD_PRODUCT_TO_CART:
const productInCart = state.products.find(
(p) => p.id === action.product.id
);
if (!productInCart)
return {
...state,
products: [...state.products, action.product],
};
updateQuantity(productInCart);
return {
...state,
products: [...state.products],
};
ADD_PRODUCT_TO_CART has to add a new item to cart if its not there already and if its there it updates its quantity.
and this is how I want to make sure the quantity of an object is increased, (later also can be decreased)
const updateQuantity = (product) => {
console.log("product quantity", product.quantity);
return product.quantity === 0
? { ...product, quantity: 1 }
: { ...product, quantity: product.quantity + 1 };
};
in my product component I have the add action after the add button is clicked:
onClick={() => props.addProduct(item)}
and also:
const mapStateToProps = (state) => {
return {
products: state.products,
};
};
const mapDispatchToProps = (dispatch) => {
return {
addProduct: (product) => dispatch(addProductToCart(product)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Product);
I can add a new item to the cart, but when I press again to add the item, it doesn't increase the quantity and when I console log the current object the quantity is 0.
updateQuantity returns a product object that supposes to increase the quanttity but it doesnt do it currectly.
I am running out of idea how should I fix this any idea?
earlier I had another queation which I removed.
Your updateQuantity function is never reached because you are return-ing before it. Run your function before you return. You might also want to set your result to a var.
if (!productInCart){
const updatedProduct = updateQuantity(productInCart);
return {
...state,
products: [...state.products, updatedProduct],
};
}

Spread operator is not working on Add To Cart function in react redux

I am trying to add the product into cart via React & Redux, here is my state functionality
Initial state
const initialState = {
error: false,
products: [],
addedItems: [],
total: 0
}
Add to cart function
case actionTypes.ADD_TO_CART:
let addedItem = state.products.find(item => item._id == action.id)
let existed_item
if(state.addedItems){
existed_item = state.addedItems.filter(item => action._id == item.id)
}
if(existed_item){
addedItem.quantity += 1
return {
...state,
total: state.total + parseFloat(addedItem.price)
}
} else {
addedItem.quantity = 1;
let newTotal = state.total + parseFloat(addedItem.price)
return {
...state,
...{addedItems: [...state.addedItems, addedItem]}, //error in here
total: newTotal
}
}
But showing this below error:
TypeError: Invalid attempt to spread non-iterable instance.
In order to be iterable, non-array objects must have a Symbol.iterator method.
I also tried like this
...{addedItems: [...(initialState.addedItems), addedItem]}
and it only adds one item to the cart, not pushing a new item.
What I have done wrong here? I really not understanding for almost 3 hours.
I really appreciate it if you help me.
Thanks
First, the outside spread opearator is not needed.
Second, you are trying to put addedItems in an object , however its a part of initialState and not a part of nested object inside initialState.
So this is what you want,
return {
...state,
addedItems: [...state.addedItems, addedItem],
total: newTotal
}
const initialState = {
error: false,
products: [],
addedItems: [],
total: 0
}
let result = {
...initialState,
addedItems: [...initialState.addedItems, 1],
total: 1
}
console.log(result)

Redux : Updating object values inside array immutably

const initialState = {
arr: [
{
name: "Chicken",
grade: "A",
quantity: 0
},
{
name: "Mutton",
grade: "B",
quantity: 0
},
{
name: "Sandwich",
grade: "A-Plus",
quantity: 0
}
]
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.ADD_QUANTITY:
return {
...state,
arr: {
...state.arr,
[state.arr[action.index]]: {
...state.arr[action.index],
[state.arr[action.index][0].quantity]:
[state.arr[action.index][0].quantity] + 1
}
}
};
default:
return state;
}
};
I'm trying to update the quantity immutably. Each time I click on a button, the quantity should be increased by 1. The above code which I have written is wrong (hence posting the query here). I'd appreciate it if anyone could show me where I went wrong and point me in the right direction.
The final output I am expecting is:
arr: [
{
name: "Chicken",
grade: "A",
quantity: 1 // Updated value
},
{
name: "Mutton",
grade: "B",
quantity: 0
},
{
name: "Sandwich",
grade: "A-Plus",
quantity: 0
}
]
There are a few things that won't work with your current code. Your initial state defines arr as an Array, but your reducer is returning an Object instead. Also, when you are trying to access the quantity key of the object inside arr, you are using an additional [0] index accessor that doesn't match with your data structure.
I would also suggest that you compose reducers (using combineReducers), to make it easier for you to keep track of the data structure. Combining reducers lets you handle individual levels of your data structure without having to worry about the whole structure. Also, using the spread operator works well for objects, but functions like map sometimes are clearer when manipulating arrays.
Something like this would do what you need:
const arr = (state = initialState.arr, action) => {
switch (action.type) {
case actionTypes.ADD_QUANTITY:
return state.map((element, i) => {
return i === action.index ? {...element, quantity: element.quantity + 1} : element;
});
default:
return state;
}
}
const rootReducer = combineReducers({arr});

Updating state with nested array of objects

This is something also called as deep state update. Where I have to update nested state array.
I am implementing a simple redux application. Here I want to update the state which is nested array of object. My reducer function takes state, action. I have to update responses property of state with new value. I tried to map/iterate the state but it isnt working for me. Is there a way to update those specific values and return update state.
const sampleData = [{
"id": 1,
"text": "Hobby",
"answers": [{
"id": 1,
"text": "Chess",
"responses": 5
}]
}];
const action = {
type: "VOTE",
questionId: 1,
answerId: 3
};
This is handleSubmit function I am calling when Submit button is clicked on form.
handleSubmit(e){
const store = createStore(hobbyReducer, hobby); // created store here
var k = (document.querySelector('input[name = "hobby"]:checked').value);
store.dispatch({type:"SUBMIT", text: k, id: 1}); // Dispatching action to reducer
store.subscribe(()=>{
console.log(store.getState());
});
}
Here is reducer part of program:
function hobbyReducer(state, action) {
switch(action.type){
case "SUBMIT":
return{
...state,
answers: state.answers.map(e=> (e.text === action.text && e.answers.id === action.id)?
{ ...answers,
responses: (e.responses+1)} :
hobby )
}
break;
default:
return state;
}
}
initial state = sampleData; // Array object given above
I am unable to update the responses property which is in a nested array
This is the code I wanted to write, after some research I finally did what was required. Although this is not solution in terms of time complexity.
`
case "SUBMIT":
const updatedState = state.map(e =>{
if(e.id === action.id)
return{...e,
answers: e.answers.map(f =>{
if(f.text === action.text){
return{
...f,
...{responses: f.responses + 1},
}
}
return f;
})
}
return e;
})
console.log("updatedstate", updatedState)
return updatedState
Just an error in your map I think:
function hobbyReducer(state, action) {
switch(action.type) {
case "SUBMIT":
return {
...state,
answers: state.answers.map(answer => {
if (answer.text === action.text && answer.id === action.id) {
answer.response = answer.response + 1;
}
return answer;
});
}
default:
return state;
}
}

React Reducer function to return array of objects

If I have an array of objects representing say a shopping cart (in React)
[ { id: 1, qty: 1 } ]
what would be a neat way of adding an item to this state ?
I would need to check if the array contains an object with that id, if it did then update it's quantity, if not then just create a new array element adding this new object.
I am getting myself in a mess trying to write this with new ES6 syntax in a functional style ... this is what I have so far ...
let initialState = []
export default function shoppingCart(state=initialState, action) {
switch(action.type) {
case 'ADD_TO_CART':
// check the array of objects and only act on the one we are interested in
let newQty = action.payload.qty
console.log("state : ", state)
for(let i = 0; i < state.length; i++) {
if (state[i].id === action.payload.id) {
newQty = state[i].qty + action.payload.qty
console.log("QTY : " + newQty)
}
}
//Obviously this will not work if the {id ...} already exists
return [ ... state, {id:action.payload.id, qty:newQty} ] // todo: update this to contain the payload with qty included
case 'REMOVE_FROM_CART':
return state.filter(elem => elem.id !== action.payload.id)
default:
return state
}
}
You can use Array.prototype.splice to replace array item with another one:
case 'ADD_TO_CART':
const index = state.findIndex(item => item.id === action.payload.id)
if (index > -1) {
state.splice(index, 1, {
id: action.payload.id,
qty: state[index].qty + action.payload.qty
})
return [...state]
}
else {
return [...state, {
id: action.payload.id,
qty: newQty
}]
}

Resources