How do I set the value of a property via a function?

Viewed 72

I'm trying to run a function inside a reducer function to calculate a cart total, but the value in the state object is the function and not the result of the function. After render, the result is displayed, but I cannot pass the object to other components (I'm using a context). How do I do it? Here is the code (assume that the values work, because they do).

I've tried setting it to the const to no avail. I've tried an anonymous function that returns the function's total, and it still doesn't work. I've also tried just calling the function.

  const reducer = (cart, action) => {
switch(action.type) {
  case("ADD_ITEM"):
    return {
      ...cart, 
      products: {
        ...cart.products,
        [action.payload.product.id]: {...action.payload.product} 
      },
      total: () => (cartTotal)
    }
  break

  case("REMOVE_ITEM"):
    delete cart.products[action.payload]
    return {
      ...cart,
      products: {
        ...cart.products
      },
      total: () => (cartTotal)
    }
  break

  case("CLEAR_CART"):
  return {
    cart: {
      ...initialState
    }
  }
  break
}
}

Here is the object:

cart: {
  products: [{}],
  total: 0
  }

Here is the function to return the total:

  const cartTotal = () => {
    const total = Object.values(cart.products).reduce((prev, curr) => {
      const currPrice = (curr.data.on_sale && curr.data.sale_price) ? curr.data.sale_price : curr.data.price
      return prev + currPrice
    }, 0)
    return total.toFixed(2)
  }

Right now, I'm passing the method that allows you to calculate the total, but it seems like it is unnecessary, as I'm watching the cart state and updating the value of total each time items are added/removed. How do I set the value of a property inside of the reducer function as the return of another helper function? Thanks!

2 Answers

I don't know why are you storing derived state, well, in state? This should be computed via a selector when reading your state out (and/or passed to a custom Context provider).

If you must store the total in state then you need to call the cartTotal function to be able to store its return value. Unfortunately this will only compute the cart total on the unupdated cart since you are currently in the function that returns the new cart state.

You can factor out the cart update so you have an updated cart products object, and with a small revision of cartTotal it can consume this updated cart products object and compute a total.

Example:

const cartTotal = (products) => {
  const total = Object.values(products).reduce((prev, curr) => {
    const currPrice = (curr.data.on_sale && curr.data.sale_price) ? curr.data.sale_price : curr.data.price;
    return prev + currPrice;
  }, 0);
  return total.toFixed(2);
};

Cases

case "ADD_ITEM": {
  const { payload } = action;

  const products = {
    ...cart.products,
    [payload.product.id]: { ...payload.product },
  }

  return {
    ...cart, 
    products,
    total: cartTotal(products),
  }
  break;
}

case "REMOVE_ITEM": {
  const products = { ...cart.products };
  delete products[action.payload];
  return {
    ...cart,
    products,
    total: cartTotal(products),
  }
  break;
}

You need to call cartTotal to calculate the value.

total: cartTotal()
Related