export const initialState = {
ids: [],
basket: [],
subtotal: 0
}
const reducer = (state, action) => {
switch (action.type) {
case 'ADD_TO_BASKET':
debugger
if (state.ids.indexOf(action.item.id) !== -1) {
// if not new item increase a quantity
console.log(state.basket.find((el) => el.id.match(action.item.id)).quantity);
++state.basket.find((el) => el.id.match(action.item.id)).quantity;
state.subtotal += action.amount
}
if (state.ids.indexOf(action.item.id) === -1) {
//if new value push id to array and push new value to basket
state.ids.push(action.item.id)
state.subtotal += action.amount
state.basket = [...state.basket, action.item]
}
return {
...state,
}
default:
return state;
}
}
I've used the debugger and where time to increase quantity it worked twice,
if I change return { ...state } to return state all works right only one-time increase, but UI isn't refreshing (basket counter)
Related
My code:
removeIndexCart: (state, action) => {
const IteamIndex_dec = state.cart.findIndex(
(iteam) => iteam.id === action.payload.id
);
if (state.cart[IteamIndex_dec].qnty >= 1) {
const dltiteams = (state.cart[IteamIndex_dec].qnty -= 1);
console.log([...state.cart, dltiteams]);
return {
...state,
cart: [...state.cart],
};
} else if (state.cart[IteamIndex_dec].qnty === 1) {
const data = state.cart.filter((el) => el.id !== action.payload);
return {
...state,
cart: data,
};
}
},
I wanted to reduce the selected products with index, but I encountered this error.
It seems that the reducer function is both trying to modify the proxy state with Immer (automatically applied by redux-toolkit), and returning a new value, which caused the error.
More about using redux-toolkit reducer with immer
Perhaps consider to always modify the proxy state in the reducer (only works in redux-toolkit where Immer is used internally), without returning a new value:
// Only works in redux-toolkit with immer
removeIndexCart: (state, action) => {
const IteamIndex_dec = state.cart.findIndex(
(iteam) => iteam.id === action.payload.id
);
if (state.cart[IteamIndex_dec].qnty > 1) {
state.cart[IteamIndex_dec].qnty -= 1;
} else if (state.cart[IteamIndex_dec].qnty === 1) {
state.cart = state.cart.filter((el) => el.id !== action.payload);
}
},
Alternatively, perhaps try always return a new value, without modifying the proxy state:
removeIndexCart: (state, action) => {
const IteamIndex_dec = state.cart.findIndex(
(iteam) => iteam.id === action.payload.id
);
if (state.cart[IteamIndex_dec].qnty > 1) {
const updatedItem = {
...state.cart[IteamIndex_dec],
qnty: state.cart[IteamIndex_dec].qnty - 1,
};
const updatedCart = [...state.cart];
updatedCart[IteamIndex_dec] = updatedItem;
return {
...state,
cart: updatedCart,
};
} else if (state.cart[IteamIndex_dec].qnty === 1) {
const data = state.cart.filter((el) => el.id !== action.payload);
return {
...state,
cart: data,
};
}
},
so my reducer looks like that.
I want to add items to cart and if the item is already in the state it should increase the qty of that item.
I pass object to payload but it doesnt contain the quantity field so i create it manually.
But when i add more than 2 of the same item it doesnt update further.
What is the issue here?
const init = { cartItems: [] };
export const cartReducer = (state = init, action) => {
switch (action.type) {
case 'ADD_TO_CART': {
const theItem = state.cartItems.find(
(item) => item.title === action.payload.title
);
if (theItem) {
theItem.quantity++;
return { ...state };
} else {
const updatedCart = [...state.cartItems, action.payload];
return { ...state, cartItems: updatedCart };
}
}
default: {
return { ...state };
}
}
};
You cannot mutate the state, it will then not update the react component, you can try the below code for updating the quantity
const init = { cartItems: [] };
export const cartReducer = (state = init, action) => {
switch (action.type) {
case 'ADD_TO_CART': {
const itemIndex = state.cartItems.findIndex(
(item) => item.title === action.payload.title
);
if (itemIndex !== -1) {
const newCartItems = [...state.cartItems];
const newItem = {...newCartItems[itemIndex]}
newItem.quantity += 1;
newCartItems[itemIndex] = newItem;
return { ...state, cartItems: newCartItems};
} else {
const updatedCart = [...state.cartItems, action.payload];
return { ...state, cartItems: updatedCart };
}
}
default: {
return { ...state };
}
}
};
EDIT: The other option you can use is with Immer js it makes state management easier, though it will add in bundle size, if you have strict budget of bundle size then you would need to think of adding Immer js, Below how you can write the logic with Immer js.
import produce from 'immer'
const init = { cartItems: [] };
export const cartReducer = (state = init, action) =>
produce(state, draft => {
switch (action.type) {
case 'ADD_TO_CART': {
const itemIndex = draft.cartItems.findIndex(
(item) => item.title === action.payload.title
);
if (itemIndex !== -1) {
draft.cartItems[itemIndex].quantity += 1;
} else {
draft.cartItems.push(action.payload)
}
break;
}
default: {
break;
}
}
})
Give it a try, Immer js Documentation, Redux and Immer
I need to increase property in the state, but I have no idea how I can do it
help please, I have tried two days but got many troubles
export const initialState = {
ids: [],
basket: []
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD_TO_BASKET':
if (state.ids.indexOf(action.item.id) === -1) {
// if new item add to basket
return {
ids: [...state.ids, action.item.id],
basket: [...state.basket, action.item]
}
}
else {
if (state.basket.length > 0) {
// state.basket[state.basket.length - 1].quantity += 1
}
return {
...state,
// IT'S WRONG
basket: [...state.basket, [state.basket.length - 1].quantity += 1]
}
}
default:
return state;
}
}
You mutate state (basket.quantity += 1) when it should treated as immutable.
I believe you need to rethink how you structure your state if it gets so hard to immutably update a state.
export const initialState = {
ids: [],
basket: [],
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case "ADD_TO_BASKET":
if (state.ids.indexOf(action.item.id) === -1) {
...
} else {
...
const basketsShallowCopy = [...state.basket];
const basketToUpdate = basketsShallowCopy.pop();
const updatedBasked = {
...baskedToUpdate,
quantity: basketToUpdate.quantity + 1,
};
basketsShallowCopy.push(updatedBasked);
return {
...state,
basket: basketsShallowCopy,
};
}
...
}
};
I've set up a Store.js with a players array. I want to add players to the array as I select them but still be able to set the array to empty if I clear the array.
Here is some code from my Store.js
const initialState = {
playerCollection: [],
}
const reducer = (state, action) => {
switch (action.type) {
case 'UPDATE_PLAYER_COLLECTION':
return { ...state, playerCollection: action.value };
default:
return state;
}
}
Here is some code from my Players.js
for(let i=0; i<players.length; i++){
let player = players[i];
if(player.position === state.posAbbr && player.status === 'ACT'){
let newPlayer = createNewPlayer(roster, player);
dispatch({ type: 'UPDATE_PLAYER_COLLECTION', value: [...state.playerCollection, newPlayer] });
return;
}
}
My dispatch line is only adding a single player to the playerCollection array.
I also want to, like I said above, be able to set the array to [] if I clear it.
Define a clear playerCollection action, and set array to empty in reducer for that action:
const reducer = (state, action) => {
switch (action.type) {
case 'UPDATE_PLAYER_COLLECTION':
return { ...state, playerCollection: action.value };
case 'CLEAR_PLAYERS':
return {...state,playerCollection: []};
case "ADD_PLAYER" :
return {...state,playerCollection : [...state.playerCollection,action.value]}
default:
return state;
}
}
I know that redux trigger react component to re-render once state of that component get changed but this doesn't happen in my situation.
Action
const addToCart = (product, cartProducts) => {
let newCartProducts = [...cartProducts, product];
newCartProducts = newCartProducts.reduce((acc, cur) => {
let repeated = acc.find(p => p.id === cur.id);
if(repeated) repeated.quantity++;
else acc.push(cur);
return acc;
}, []);
return {
type: ADD_TO_CART,
payload: newCartProducts
}
}
Reducer:
export default (state = [], action) => {
switch (action.type) {
case ADD_TO_CART:
return action.payload;
default:
return state;
}
}
The reducer returns a new state every time the action dispatched from the component but i need to close the cart and open it again to get the effect, redux does not update the product quantity simultaneously??
You are modifying the existing elements in the state.
Use
newCartProducts = newCartProducts.reduce((acc, cur) => {
let repeatedIndex = acc.findIndex(p => p.id === cur.id);
const repeated = acc[repeatedIndex];
if (repeated) {
acc[repeatedIndex] = {
...repeated,
quantity: repeated.quantity + 1
};
} else acc.push(cur);
return acc;
}, []);
You array is recreated each time, but the objects inside it are not. So when you modify their internals you need to notify that the specific object has changed.
Refactor logic to the reducer and set the quantity here:
const addToCart = product => {
return {
type: ADD_TO_CART,
payload: product,
};
};
//I assume state is an array of products on your cart
export default (state = [], action) => {
switch (action.type) {
case ADD_TO_CART:
const { id } = action.payload;
return state.map(p => p.id).includes(id)
? //product is already on card add quanity
state.map(p =>
p.id === id
? { ...p, quantity: p.quantity + 1 }
: p
)
: state.concat({ ...action.payload, quantity: 1 }); // add product
default:
return state;
}
};