Gautam Patadiya
Gautam Patadiya

Reputation: 1412

why state updating itself? Without calling setState

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

Before Method call enter image description here

After method call: enter image description here

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

Answers (3)

Sagiv b.g
Sagiv b.g

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

UtkarshPramodGupta
UtkarshPramodGupta

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

Ravindra Thorat
Ravindra Thorat

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

Related Questions