0

I am new in Vuejs, but I'm coming from React, just trying to use Vuex here.

I built my store with my state containing an array cart.

const state = () => ({
    cart: []
})

And I want a function isInCart(product) that returns either the product is in the current cart or not.

<span v-if="isInCart(product)">hello</span>

Am I supposed to use the getters of my store ?

const getters = {
    isInCart: (state) => (product) => state.cart.find(_product => _product.id === product.id) !== undefined,
}

And then use it like this in my component ?

computed: {
    ...mapGetters(['isInCart'])
},

But I have : Computed property "isInCart" was assigned to but it has no setter..

EDIT

I forgot to prefix the getters with its module name.

...mapGetters({ isInCart: 'shop/isInCart' }) // module name = shop
robinvrd
  • 1,760
  • 12
  • 28

2 Answers2

1

Ref: Am I supposed to use the getters of my store ?

Yes. ...mapGetters({}) only reads getters from your store.

If you want to read state properties, (i.e: cart), use

computed: {
  ...mapState({ cart })
}

...or

computed: {
  cart() {
    return this.$store.state.cart
  }
}

Ref: Computed property "isInCart" was assigned to but it has no setter.

It's because you are trying to assign a value to isInCart inside the component where you're importing it.

You can't do that. If you ever want to actually set a store getter to a particular value, here's the pattern to use:

computed: {
  yourGetter: {
    get: function() {
      return this.$store.getters['yourGetter']
    },
    set: function(value) {
      this.$store.dispatch('actionCommittingMutationChangingYourGetter', value)
    }
  }
}

Note: if you only need that getter in this component, it doesn't have to be a store getter. You can get it directly from state:

computed: {
  whatever: {
    get: function() {
      return this.$store.state.whatever;
    },
    set: function(value) {
      this.$store.dispatch('setWhatever', value);
    }
  }
}

Note: If your getter returns a function (as in your case) you can safely call it after mapping it in the component as computed: (i.e: this.isInCart(product)). It's unusual to use a computed with a param, but if it returns a fn that fn can be called.

On another note, in many cases, you can skip the dispatch and commit directly. But committing directly from outside of a store is not considered "safe" and in certain cases you'll get errors for doing it.
Out of principle, I would advise against it, although it does work in some cases.


Ref how to write your fn cleaner. This discussion involves a lot of personal preference as well as coding standards you or your team might have in place. Which makes the discussion per-se off topic on Stack Overflow.

Just for kicks, here's how I'd write it. But it's not because it's better or cleaner. It's based on purely personal preference and it's likely faster than what you have by an insignificant amount:

const getters = {
  isInCart: state => product => state.cart.map(p => p.id) 
    .findIndex(id => id === product.id) > -1
}
tao
  • 82,996
  • 16
  • 114
  • 150
  • Your answer is full of good content but not really answering my issue on how to build the `isInCart(product)` function cleaner. – robinvrd Jun 12 '20 at 10:54
  • 1
    @robinvrd, first off, *"How to write the function cleaner?"* is off-topic on SO. It might be a good question on [code review](https://codereview.stackexchange.com/). Another problem is you're asking more than one question. The one above and the one about the error. And last, but not least, you haven't included the code that produces the error (If I just use your code I'm not getting that error). Despite all of reasons above to close the question as off-topic, I tried to help by providing useful insight. Not sure how I could help more, considering current state of question. – tao Jun 12 '20 at 11:54
  • Provide a [mcve] and I'll have a look. Use codesanbox.io or similar if you need a multi-file node based environment. – tao Jun 12 '20 at 11:59
  • My mistake was to not prefix my getter by its module... Dumb thing a newbie only could do. – robinvrd Jun 12 '20 at 13:22
  • Happens to us all. For some reason, people expect experienced devs not to make silly mistakes. It happens all the time. It's just you put more tools in place to discover them sooner rather than later. That's the only difference, I'd say. – tao Jun 12 '20 at 13:51
0

Take a look at this answer for a Vuex getter with parameters: Pass params to mapGetters

Edit

I agree, it is not clean and not simple. I would much rather redesign this:

Option 1

You can use your cart from Vuex and calculate directly, keeping reactivity:

<template>
  <span v-if="$store.state.cart.some(p => p.sku === productSku)">hello</span>
  <!—- both work efficiently —>
  <span v-if="hasProduct(productSku)">hello</span>

</template>
<script>
export default {
  methods: {
    hasProduct(sku) {
      return this.$store.state.cart.some(p => p.sku === sku)
    }
  }
}
</script>

That being said, this is not a computed property. You want to have multiple return values of a computed property, but it is not designed for this purpose.

Option 2

Your best option would be to introduce a product relevant version of this component:

<template>
  <span v-if="hasProduct">hello</span>
<template>

<script>
export default {
  name: „ProductRelevantComponent“
  props: {
    productSku: {
      type: String,
      required: true
    }
  },
  computed: {
    hasProduct() {
      return this.$store.state.cart.some(p => p.sku === this.productSku)
    }
  }
}
</script>
János Veres
  • 187
  • 1
  • 7