Reputation: 1412
I'm beginner of React. Facing one problem don't know why. Have one array of products into react state.
When user click on button(Through the sub component) I'm cloning current product array from state and then modifying it but state is updating without calling setState. Help really appreciate. I want to know why the it's behaving like this.
Here is App component file:
class App extends Component {
constructor(props) {
super(props);
this.state = {
products: [
{
id: 1,
img: 'http://placehold.it/460x250/e67e22/ffffff&text=HTML5',
title: 'Laptop',
quantity: 1,
price: 250,
},
],
cart_products: [],
total_price: 0,
total_product: 0,
};
}
addProductToCart = (id) => {
let cartProducts = [];
// Trying to clone first
let productCopy = [...this.state.products];
// Find specific product
let newProduct = productCopy.find((item) => {
return item.id === id;
});
// Now into new product I want to add some extra property
newProduct['originalPrice'] = newProduct.price;
// Here I want some other code.
cartProducts.push(newProduct);
this.setState({
cart_products: cartProducts
});
}
render() {
return (
<div className="container-fluid">
<div className="container">
<h1 className="text-center">React.js Example </h1>
</div>
<div>
<div className="col-sm-6">
<Cart products={this.state.cart_products} totalPrice={this.state.total_price} totalProduct={this.state.total_product} />
</div>
<div className="col-sm-6">
<Products products={this.state.products} onClicked={this.addProductToCart} />
</div>
</div>
</div>
);
}
}
Problem is "products" array into state automatic updating. Here is the picture of React Chrome Extension
Idea of application is just simple cart system. when user click on add product button I want to add specific product into cart. But I'm stuck how to fix this issue.
Upvotes: 1
Views: 557
Reputation: 31024
You can use the spread object syntax to create new properties without mutating the original object in the state:
addProductToCart = (id) => {
const { products, cart_products } = this.state;
// Find the relevant product
let relevantProduct = products.find((item) => {
return item.id === id;
});
// Now into new product I want to add some extra property
const newProduct= {
...relevantProduct,
originalPrice: relevantProduct.price
};
this.setState({
cart_products: [...cart_products, newProduct]
});
}
Running example:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
products: [
{
id: 1,
img: 'http://placehold.it/460x250/e67e22/ffffff&text=HTML5',
title: 'Laptop',
quantity: 1,
price: 250,
},
],
cart_products: [],
total_price: 0,
total_product: 0,
};
}
addProductToCart = (id) => {
const { products, cart_products } = this.state;
// Find the relevant product
let relevantProduct = products.find((item) => {
return item.id === id;
});
// Now into new product I want to add some extra property
const newProduct = {
...relevantProduct,
originalPrice: relevantProduct.price
};
this.setState({
cart_products: [...cart_products, newProduct]
});
}
render() {
const { products, cart_products } = this.state;
return (
<div>
<div>Cart</div>
{
cart_products.map((product, index) => <div key={index}>{product.title}</div>)
}
<hr />
<div>Product list</div>
{
products.map((product, index) => (
<div>
<div key={index}>{product.title}</div>
<button onClick={() => this.addProductToCart(product.id)}>Add</button>
</div>)
)
}
</div>
)
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Upvotes: 1
Reputation: 8162
In JS objects are passed by reference.
I'm not sure if you already know this fact but in JS and I guess almost all programming languages, objects are passed by reference. In your code on the following line:
// Trying to clone first
let productCopy = [...this.state.products];
...you are creating a new array, productCopy
, for sure, but even the new array would be having those same exact objects from the previous array this.state.products
and not some clone of those objects as you are expecting.
You can write your own code for deep cloning of object and arrays. Or, you can make use of some really good JS library like lodash as follows:
var arr = [{a:1, b:2}, {c:3, d:4}]
var newArr = _.cloneDeep(arr);
newArr[0].e = 5;
console.log(arr[0]);
console.log(newArr[0]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Upvotes: 0
Reputation: 2002
I can see some problem at line
let productCopy = [...this.state.products];
just replace the line with,
let productCopy = JSON.parse(JSON.stringify([...this.state.products]))
why was it needed?
that's how the spread operator works while copying the complex variables. here is the reference which says,
Note: Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays as the following example shows (it's the same with Object.assign() and spread syntax).
Upvotes: 2