There is a function that adds objects product to products in state
addProductToCart(productId, productName)
{
var product = {
id: productId,
name: productName,
};
this.setState({
products: {
...this.state.products,
[product.id]: product
}
});
}
but these objects are sorted by [product.id]. How do I sort them in the order they are added to the cart?
maintain one more array ids in which, you can append the productIds as they are added :
addProductToCart(productId, productName)
{
var product = {
id: productId,
name: productName,
};
this.setState({
products: {
...this.state.products,
[product.id]: product
},
productIds : [...ids, product.id]
});
}
You can then iterate over the array to retrieve the product in the order of their insertion.
If you read this objects are by default key value pairs,
You have 2 options you can use #Easwar solution or you can use array instead to store.
As far as I see your strucutre there is nothing wrong in using the array structure for your requirement.
You should restructure your state like this
constructor() {
super();
this.addProductToCart = this.addProductToCart.bind(this);
this.state = {
products: []
};
}
addProductToCart() {
productsId--;
var product = {
id: productsId,
name: 'test',
};
this.setState({
products: [...this.state.products,product]
});
}
Demo
As I can see you have a problem getting removed object from array you can use this easily
removefromCart(value) {
var array = [...this.state.products]; // make a separate copy of the array
var index = array.findIndex(a => a.id === value);
if (index !== -1) {
array.splice(index, 1);
this.setState({ products: array });
}
}
Related
If you have an array as part of your state, and that array contains objects, whats an easy way to update the state with a change to one of those objects?
Example, modified from the tutorial on react:
var CommentBox = React.createClass({
getInitialState: function() {
return {data: [
{ id: 1, author: "john", text: "foo" },
{ id: 2, author: "bob", text: "bar" }
]};
},
handleCommentEdit: function(id, text) {
var existingComment = this.state.data.filter({ function(c) { c.id == id; }).first();
var updatedComments = ??; // not sure how to do this
this.setState({data: updatedComments});
}
}
I quite like doing this with Object.assign rather than the immutability helpers.
handleCommentEdit: function(id, text) {
this.setState({
data: this.state.data.map(el => (el.id === id ? Object.assign({}, el, { text }) : el))
});
}
I just think this is much more succinct than splice and doesn't require knowing an index or explicitly handling the not found case.
If you are feeling all ES2018, you can also do this with spread instead of Object.assign
this.setState({
data: this.state.data.map(el => (el.id === id ? {...el, text} : el))
});
While updating state the key part is to treat it as if it is immutable. Any solution would work fine if you can guarantee it.
Here is my solution using immutability-helper:
jsFiddle:
var update = require('immutability-helper');
handleCommentEdit: function(id, text) {
var data = this.state.data;
var commentIndex = data.findIndex(function(c) {
return c.id == id;
});
var updatedComment = update(data[commentIndex], {text: {$set: text}});
var newData = update(data, {
$splice: [[commentIndex, 1, updatedComment]]
});
this.setState({data: newData});
},
Following questions about state arrays may also help:
Correct modification of state arrays in ReactJS
what is the preferred way to mutate a React state?
I'm trying to explain better how to do this AND what's going on.
First, find the index of the element you're replacing in the state array.
Second, update the element at that index
Third, call setState with the new collection
import update from 'immutability-helper';
// this.state = { employees: [{id: 1, name: 'Obama'}, {id: 2, name: 'Trump'}] }
updateEmployee(employee) {
const index = this.state.employees.findIndex((emp) => emp.id === employee.id);
const updatedEmployees = update(this.state.employees, {$splice: [[index, 1, employee]]}); // array.splice(start, deleteCount, item1)
this.setState({employees: updatedEmployees});
}
Edit: there's a much better way to do this w/o a 3rd party library
const index = this.state.employees.findIndex(emp => emp.id === employee.id);
employees = [...this.state.employees]; // important to create a copy, otherwise you'll modify state outside of setState call
employees[index] = employee;
this.setState({employees});
You can do this with multiple way, I am going to show you that I mostly used. When I am working with arrays in react usually I pass a custom attribute with current index value, in the example below I have passed data-index attribute, data- is html 5 convention.
Ex:
//handleChange method.
handleChange(e){
const {name, value} = e,
index = e.target.getAttribute('data-index'), //custom attribute value
updatedObj = Object.assign({}, this.state.arr[i],{[name]: value});
//update state value.
this.setState({
arr: [
...this.state.arr.slice(0, index),
updatedObj,
...this.state.arr.slice(index + 1)
]
})
}
I have a state object with a category array in it. In this array of objects, one of the keys ("list") of the object is assigned an array.
What I need to do is
filter a specific object in the category array by name
add a new object to the "list" property (array) of the filtered object (keeping the old ones)
Example
const initialState = {
category: [
{
name: "new",
color: "#5236C1",
list: [
{
title: "name title",
about: "about",
imgLink: 'https://www.google.com/s2/favicons?domain=https://mail.yandex.ru/',
url: 'https://www.yandex.ru'
},
// there should be a new object
]
},
tried to do so
const addBM = (state, payload) => {
console.log(payload)
const {url, title, about, cat} = payload
let selectCatArr = state.category.filter((item) => item.name == cat)
return {
...state,
category: [...state.category, {list: [title, url, about ]}]
}
}
there should be a new object
I have modifed the addBM function and added some comments which explain the code.
const addBM = (state, payload) => {
console.log(payload)
const {url, title, about, cat} = payload
// use map method instead of filter
const modifiedCategory = state.category.map(categoryItem => {
// only modify the category item if it's name matches the cat argument
// otherwise return the original category item
if (categoryItem.name == cat) {
// create a copy of the category item object
let modifiedCategoryItem = {
...categoryItem,
// modify the list array of this object by
// creating a copy of the array and
// adding a new object with (url, title and about arguments) in this array
list: [...categoryItem.list, {url, title, about}]
};
return modifiedCategoryItem;
} else {
return categoryItem;
}
});
return {
...state,
category: modifiedCategory
}
};
I am trying to increment the productQuantity of an item that has been pushed into an array. If the productID of the item matches that of an item already in the array, the quantity should be increased.
export function ADD_ITEM(state, product) {
// state.isAdded = true;
const added = state.storeCart.find(product => product ===
product.productID)
if (!added) {
state.storeCart.push(product)
} else {
product.productQuantity++
}
It looks like there are a few issues.
The product argument in your find callback function is shadowing the product argument from the ADD_ITEM function. We'll change this to p.
In the find callback, you should check if the productID of p is equal to that of the provided product.
It seems that you just want to push the entire product onto the storeCart, not push each individual property.
You should increment productQuantity on added since that's a ref to the actual existing item in state.
export function ADD_ITEM(state, product) {
const added = state.storeCart.find(p => p.productID === product.productID);
if (!added) {
state.storeCart.push(product);
} else {
added.productQuantity++;
}
}
Just to demonstrate that this is functional, here's a bare-bones example.
function ADD_ITEM(state, product) {
const added = state.storeCart.find(p => p.productID === product.productID);
if (!added) {
state.storeCart.push({...product});
} else {
added.productQuantity++;
}
}
const state = {
storeCart: []
}
const hat = { productID: 1, name: "hat", productQuantity: 1 };
const jacket = { productID: 2, name: "jacket", productQuantity: 1 };
const shoes = { productID: 3, name: "shoes", productQuantity: 1 };
ADD_ITEM(state, hat);
ADD_ITEM(state, jacket);
ADD_ITEM(state, shoes);
ADD_ITEM(state, jacket);
console.log(state);
I've got an object of type : [ {name : 'xxx' , price: '555', quantity : '2' } , {...} ] and so one.
I got a class
getCartItems() {
let items = localStorage.getItem('item');
items = JSON.parse(items);
return items;
}
where i get this array.
Now i am getting index of the array, for example 0 , it should remove first array from object.
but when i do .remove, or other, it does not work. this.getCartItems()[index].remove or other does not work. Can you help me?
My guess is that you are mutating the object after you parse it and you never save it back.
You have to save the mutated object inside of your localStorage to make your removal of the first item persistant.
Look at the following example :
const localStorage = {
items: {
item: JSON.stringify([{
name: 'xxx',
price: '555',
quantity: '2',
}, {
name: 'yyy',
price: '666',
quantity: '5',
}, {
name: 'zzz',
price: '777',
quantity: '6',
}]),
},
getItem: str => localStorage.items[str],
setItem: (str, value) => {
localStorage.items[str] = value;
},
};
function getCartItems() {
const items = localStorage.getItem('item');
const parsedItems = JSON.parse(items);
// We remove the first element
const item = parsedItems.splice(0, 1);
// We save the value
localStorage.setItem('item', JSON.stringify(parsedItems));
return item;
}
console.log('First call ---');
console.log(getCartItems());
console.log('');
console.log('Second call ---');
console.log(getCartItems());
console.log('');
console.log('Third call ---');
console.log(getCartItems());
Use filter to get required items. In the following updated will not have earlier 0 index item. Now, the updated array you may want to set in localStorage again if required.
const items = getCartItems();
const indexToRemove = 0;
const updated = items.filter((,index) => index !== indexToRemove);
You can use array method filter to remove the object from array. This can look something like this:
getCartItems() {
let items = localStorage.getItem('item');
items = JSON.parse(items);
return items;
}
removeCart(){
return id; // the id that you will have from your a tag
}
const updatedItems = this.getCartItems().filter((item,index) => index !== this.removeCart()); // in updated items you will find your filtered out array of object
I'm working on a table planner app where guests can be assigned to dinner tables.
I have created an object array in the state called tabledata, which will contain objects like so:
this.state = {
tabledata: [
{
name: "Top Table",
guests: ["guest1", "guest2", "guest3"]
},
{
name: "Table One",
guests: ["guest3", "guest4", "guest5"]
}
]
}
I am then creating a drag and drop interface where guests can move between tables. I have attempted to update the state like so:
updateTableList (tablename, guest) {
const selectedTableObj = this.state.tabledata.filter((tableObj) => tableObj.name === tablename);
const otherTableObjs = this.state.tabledata.filter((tableObj) => tableObj.name !== tablename);
selectedTableObj[0].guests.push(guest);
const updatedObjectArray = [...otherTableObjs, selectedTableObj];
this.setState({
tabledata: [...otherTableObjs, ...selectedTableObj]
});
}
This works but because I am removing selectedTableObj from the state and then adding it to the end of the array I'm getting some funky results on screen. The updated table always goes to the bottom of the page (as you'd expect).
How can I update the object without changing its position within the array?
Find the index of the table you want to update using Array.findIndex(). Create a new tabledata array. Use Array.slice() to get the items before and after the updated table, and spread them into the new tabledata array. Create a new table object using object spread, add the updated guests array, and add the table object between the previous items:
Code (not tested):
updateTableList(tablename, guest) {
this.setState((prevState) => {
const tableData = prevState.tabledata;
const selectedTableIndex = tableData.findIndex((tableObj) => tableObj.name === tablename);
const updatedTable = tableData[selectedTableIndex];
return {
tabledata: [
...prevState.tabledata.slice(0, selectedTableIndex),
{
...updatedTable,
guests: [...updatedTable.guests, guest]
},
...prevState.tabledata.slice(selectedTableIndex + 1)
]
};
});
}
selectedTableObj[0].guests.push(guest) directly mutates the state which is not encouraged in React.
Try this:
this.setState((prevState) => {
const newData = [...prevState.tabledata];
// if you pass in `index` as argument instead of `tablename` then this will not be needed
const index = prevState.tabledata.findIndex(table => tableObj.name === tablename);
newData[index] = {
...newData[index],
guests: newData[index].guests.concat([guest]),
};
return { tabledata: newData };
});
You also did not remove the guest from its previous table so you need to modify for that.
You can do it with a Array.reduce
let newState = this.state
// let newState = {...this.state} // in case you want everything immutable
newState.tableData = newState.tableData.reduce((acc, table) =>
if(table.name === tableName) {
return acc.concat({...table, guests: table.guests.concat(newGuest)})
} else {
return acc.concat(table)
}
)