My result is seeing item.acf.count as string and not as number. How can I convert this to number?
Here is the function below.
increaseItem = (id) => {
const { cart } = this.state;
cart.forEach(item => {
if (item.id === id) {
item.acf.count += 1
}
})
this.setState({
cart:cart
})
}
My result is seeing item.acf.count as string and not as number. please
how can I convert is to number
You should ensure that the initial item.acf.count state is a number type so this count: item.acf.count + 1 operation works correctly and returns a number type. So long as your state updaters maintain the state invariant of item.acf.count being a number it should work as expected.
Additionally, the increaseItem handler is mutating the cart state and not creating new array/object references.
increaseItem = (id) => {
const { cart } = this.state; // <-- cart is reference to cart state
cart.forEach(item => {
if (item.id === id) {
item.acf.count += 1; // <-- mutation!
}
});
this.setState({
cart: cart // <-- cart state reference back into state
})
}
You should instead shallow copy the cart and then also shallow copy the cart item (any any other nested properties) you want to update. I also suggest using a functional state update so you are correctly updating from the previous state and not any state value closed over in increaseItem callback scope.
increaseItem = (id) => {
this.setState(prevState => ({
cart: prevState.cart.map(item => item.id === id
? {
...item,
acf: {
...item.acf,
count: item.acf.count + 1
}
}
: item),
}));
}
Related
I have this cart state in which the initial value is an empty array [].
const [cart,setCart] = useState([]);
This is how one of my product object looks like,
{id: 1, name: 'Shoe pair', price: 40}
There is an add to cart button for each product. So when the add to cart button is clicked addToCart function is triggered,
const addToCart = (item) => {
let initialItem = {id: item.id, name: item.name, quantity: 1}
let existingItem = cart.filter(cartItem => item.id === cartItem.id);
if(existingItem.length > 0){
existingItem.quantity = existingItem.quantity + 1;
} else {
setCart(crr => [...crr, initialItem ]);
}
}
What does addToCart do?
As you can see it is simple.First, it creates an object by setting the initial quantity to 1. If already the same product is present in the cart it updates the quantity in the cart product by 1 else the initialItem being added to the cart.
To monitor this occurrence I used useEffect hook,
useEffect(() => {
console.log(cart);
}, [cart]);
My problem is I can't see the cart in the console log when the quantity updates by 1 , But it shows when the initialItem is being pushed to the cart.
First issue: It is find, not filter.
Next issue - modifying item inside of array will not tell React that array is changed, you need to re-set state after existing item update also.
const addToCart = (item) => {
const initialItem = { id: item.id, name: item.name, quantity: 1 };
const existingItem = cart.find((cartItem) => item.id === cartItem.id);
if (existingItem) {
existingItem.quantity += 1;
setCart((curr) => [...curr]);
} else {
setCart((curr) => [...curr, initialItem]);
}
};
The reason your useEffect is not running when you think it should, is because its dependency is not being updated when you think it is. It will run when setCart is called and the reference to cart is updated, and then you will see your console log.
filter returns a new array -- will not mutate the original array.
docs -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
find returns the item by reference, if found -- otherwise returns undeifined.
docs -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
Alternate example:
const addToCart = (item) => {
const existingItem = cart.find(i => i.id === item.id)
const updatedCart = existingItem
? cart.map(i => {
return i.id === item.id ? {...i, quantity: i.quantity + 1} : i
})
: [...cart, item]
}
setCart(updatedCart)
}
I am making a shopping cart - onClick I have redux adding items to cartItems array.
In the code below (increment reducer its the last one after add/remove) I am trying to get rid of duplicate values from the cartItems array which holds all the items added to the shopping cart, and display a total number of unique items in the cart with cartIcon: {value: 0} - which is by default 0 (before adding any items).
const initialState = {
cartItems: [],
cartQuantity: 0,
cartIcon: {value: 0},
}
export const addToCartSlice = createSlice({
name: 'cart',
initialState,
reducers: {
add(state, action ) {
const itemIndex = state.cartItems.findIndex(
(props) => props.id === action.payload.id
);
if(itemIndex >= 0){
state.cartItems[itemIndex].cartQuantity += 1;
} else {
const tempProduct = {...action.payload, cartQuantity: 1}
state.cartItems.push(tempProduct);
}
},
remove(state, action) {
const removeItem = state.cartItems.filter(
(cartItem) => cartItem.id !== action.payload.id
);
state.cartItems = removeItem;
},
increment: (state) => {
const Items = state.cartItems.filter(
(element, index) => state.cartItems.indexOf(element) === index);
state.value = Items.length;
} // if i just do state.value += 1
// then the value goes up by 1
// but I want to display the amount of unique entries
},
});
Here onClick I am pulling data from the item that was "added" to the cart and additionally trying to increment the cartIcon number by 1 (if the item hasn't been yet added to the array cartItems). The problem could be here? Because the error mentions all the props and data I'm pulling to be rendered into the cart.
const dispatch = useDispatch()
const handleAddToCart = (props) => {
dispatch(add(props));
};
return (<>
<div id={props.id} className='shopitem'>
<img src={props.url} />
<h2>{props.title}</h2>
<p className='boldprice'>${props.price}</p>
<button onClick={() => {
handleAddToCart(props);
dispatch(increment())
}}> ADD TO CART </button>
</div>
</>
)
}
And here I am trying to display the amount of unique items to the shopping cart icon.
const count = useSelector((state) => state.cart.cartIcon.value)
{count}
For some reason I am getting this error. If I just do state.value += 1 it will add +1 to the shopping cart icon, however I only want to display +1 for each unique item.
"Uncaught Error: Objects are not valid as a React child (found: object with keys {id, title, price, url, cartQuantity}). If you meant to render a collection of children, use an array instead."
Please help - I am relatively new to Javascript and programming overall.. I may be making a stupid mistake, so if something is clearly wrong.. then please let me know :)
So, I define 2 states with the same array, it's listRasioUom and showListRasioUom. but when I setstate to listRasioUom state, the showListRasioUom state also changed with the same value as listRasioUom. any help? this is my code
handleChange = (e) => {
if (["rasio", "uom"].includes(e.target.name)) {
let listRasioUom = this.state.listRasioUom
if(listRasioUom.some(el => el.uom === e.target.value))
{
alert('Duplicate uom')
}
else
listRasioUom[e.target.dataset.id][e.target.name] = e.target.value;
this.setState({ listRasioUom })
}
}
}
showListRasioUom state is used for fetching the data on my datatables, while listRasioUom state is used to modified the state, so i only want to change listrasiouom state, but when i use setstate on listRasioUom state, the showListRasioUom also change.
<MDBDataTable
info={false}
paging={false}
searching={false}
striped
bordered
small
data={{columns: this.state.columns, rows: this.state.showListRasioUom}}
noBottomColumns
/>
Object and arrayss in javascript are assigned by reference and if you mutate the original array the other array which also holds the same reference is also updated
You should update your data in an immutable manner
handleChange = (e) => {
if (["rasio", "uom"].includes(e.target.name)) {
let listRasioUom = this.state.listRasioUom
if(listRasioUom.some(el => el.uom === e.target.value))
{
alert('Duplicate uom')
}
else {
const id = e.target.dataset.id;
const {name,value} = e.target;
this.setState(prev => ({
listRasioUom: prev.listRasioUom.map((item, i) => {
if(i == id) return {...item, [name]: value};
})
return item;
}))
}
}
}
My redux state has a SessionList which is an array of SessionObjects, and each SessionObject has an array of HandObjects.
I use state to update individual SessionObjects by adding new HandObjects, however I want to update the redux store with my updated SessionObject (immutably, if possible).
the index of the SessionObject within SessionList is action.payload.Id (I think? i will show my SessionObject constructor)
**Adding Sessions works just fine, and I only update a Session with the session already in the store
I have tried every link that I can find, but my code is just different enough that I can't seem to update my SessionList correctly.
my reducer, with the initial state
store.js (where my reducers are)
const initialState = {
SessionList: [],
}
...
case "UPDATE_SESSION":
//action.payload is a SessionObject
//action.payload.Id is the Id that i want to update
// i want to set SessionList[action.payload.Id] = action.payload
state = {
...state,
SessionList : state.SessionList.map((item, index) => {
if (index != action.payload.id) {
return item
}
return action.payload
//This code doesn't visibly do anything that I can find
})
// *******FIRST TRY*******
// ...state,
// SessionList: {
// ...state.SessionList,
// SessionList[action.payload.id] : action.payload
//syntax error, but basically what i want to do
// }
// }
// ***********************
};
break;
SessionObject constructor
Session.js
export function SessionObject(_id, _name, _hands) {
this.id = _id, //int
this.name = _name, //string
this.HandList = _hands //array of HandObject objects
}
var defaultSession = new SessionObject(0, "default", []);
*in case i am doing this wrong,
I call
(Session.js)
this.props.navigation.state.params.updateMethod(this.state.Session); //update store
from a stack-navigator child component.
then in the parent component,
I call
(Home.js)
UpdateSession = (session) => {
this.props.updateSession(session);
};
With my dispatcher looking like:
(Home.js)
updateSession: (session) => {
dispatch({
type: "UPDATE_SESSION",
payload: session
});
}
From testing, I am somewhat sure my reducer is getting called correctly.
I want to replace the SessionObject with action.payload in the correct index of the SessionList.
EDIT *
Session.js
addItem = () => {
this.setState((state, props) => ({
Session : {
...state,
HandList: [...state.Session.HandList, new HandObject(state.HandCount)]
},
HandCount : state.HandCount + 1,
}));
this.props.navigation.state.params.updateMethod(this.state.Session); //update store
};
The state in Session.js is a SessionObject
To achieve a result of SessionList[action.payload.Id], you need to initialise SessionList as an object and not an array. Then you can update accordingly.
const initialState = {
SessionList: {},
}
case "UPDATE_SESSION":
state = {
...state,
SessionList: Object.keys(state.SessionList).reduce((acc, id) => {
if (id === action.payload.Id) {
acc[id] = action.payload;
} else {
acc[id] = state.SessionList[id];
}
return acc;
}, {});
};
Update
As requested here, following are the add, update, replace and delete handlers without changing SessionList to an object.
const initialState = {
SessionList: [],
}
Note: action.payload (wherever used) is a session object.
Add
state = {
...state,
SessionList: [...state.SessionList, action.payload];
};
Update
state = {
...state,
SessionList: state.SessionList.map((session) => {
if (session.Id === action.payload.Id) {
return Object.assign({}, session, action.payload);
}
return session;
})
};
Replace
state = {
...state,
SessionList: state.SessionList.map((session) => {
if (session.Id === action.payload.Id) {
return action.payload;
}
return session;
})
};
Delete
state = {
...state,
SessionList: state.SessionList.filter((session) => session.Id !== action.payload.Id)
};
I have this class:
class Board {
this.state = {
lists : [{
id: 0,
title: 'To Do',
cards : [{id : 0}]
}]
}
And want to use setState on the 'cards' array inside of the 'lists' state array. Previously, I had the cards array in a child component but I have now moved it up to the Board class. This is the function that I had before.
deleteCards(id){
this.setState({
cards: this.state.cards.filter(card => card.id !== id)
});
}
How can I change it so that it works now that cards is inside another array?
I was unable to solve it looking at these posts:
ReactJS - setState of Object key in Array
How to edit an item in a state array?
To do it all within setState (note that the first argument to setState is an updater function where its first argument is a reference to the previous state):
If you can provide the listId from the caller:
deleteCards(listId, cardId) {
this.setState(prevState => ({
lists: prevState.lists.map((list) => {
if (list.id !== listId) {
return list
}
return {
...list,
cards: list.cards.filter(card => card.id !== cardId)
}
})
}))
}
If you can not provide the listId from the caller:
deleteCards(id) {
this.setState(prevState => ({
lists: prevState.lists.map((list) => {
if (list.cards.some(card => card.id === id)) {
return {
...list,
cards: list.cards.filter(card => card.id !== id)
}
}
return list
})
}))
}
You should attempt to use the new rest and spread syntax...
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
const newListItem = {this.state.lists[0].cards.filter....}
this.setState({lists: [...this.state.lists.cards, newListItem]})
I would have made this a comment but it would be pretty hard to read. This is just an example you need to actually write a filter.