reactjssetstatecustom-events

React, conditional setState with the click of a button


So, I don't know if how I want this to work is possible at all, but in my head I think it could I just can't seem to get it to work..

Basically I have an empty array of objects:

this.state = {
     orders: [{}]
}

Then when I click on a button I want an order to be added (custom event because cart has no parent-child relations to the button clicked, for reasons):

pubSub.on("addToCart", (data) =>
   this.setState((prevState => ({
      orders: [...prevState.orders, data.order]
   })
);

This works. I end up with:

orders: [
 {[sku]: sku, quantity: quantity}
]

Now the problem I am stuck with is that if an order is to be added with an SKU that is already in the array, I want just the quantity to be updated. If the SKU isn't already found in the array, I want a new order added. So I want to check the array for the existence of a key (key SKU is the same as the value of key SKU).

So, to check an array for the existence of a key I would use:

this.state.orders.map(obj =>{
  if (!(data.sku in obj)){
    **setState here with order added**
  }
})

I know how the conditional should look like if it finds that the key exists, and then only update the quantity.

the problem I face is that it iterates over all the orders in the array, and every time the key doesn't exist, it adds a new order. How could I make this, so it checks for the existence of a key, and only adds a new order 1 time if that's the case, and not every time? I want actually to first check all the keys, and then decide what to do. With this approach, I have to decide every round of the map what should be done.

Also keep in mind that we are in the scope of a custom event handler, where I want to use data. inside this handler, I cannot assign variables outside the scope of the map function. So basically it's or I use just setState, or I use the map function and inside there the setState. I can't use them both next to each other as data is only available in the first method call. This is also the first time I try to use this custom event handler thing, and the scoping has tied my hands a bit.

If this makes sense, and someone can point me in the right direction, that would be greatly appreciated. Thanks in advance!


Solution

  • You can pass function in setState. This function will have current state and, if you use classes, current props. In this function you create state change - part of state object that you want to update. In your case it might look like this (it's barebones and dirty, to show how you can do it):

    // data is here, somwhere
    this.setState(function(state, props) {
      let index = state.orders.findIndex(obj => data.sku in obj);
      if (index > -1) {
        // update existing
        return {orders: state.orders.slice(0, index).concat(
          {...state.orders[index], quantity: state.orders[index].quantitiy + 1},
          state.orders.slice(index+1))}
      } else {
        // create new
        return {orders: state.orders.concat(data)}
      }
    })