Vue.js Force computed properties to recompute?

Solution 1:

There is no need to force a computed value to update. A computed property will always be recomputed automatically if a needed value is changed.

So, in your case, you will need to change singlePlaces and filteredPlaces will automatically be updated.

There is a thing to note about Array and Object reactivity as seen in the doc (https://vuejs.org/v2/guide/reactivity.html#For-Arrays).

In your example, you did not include the code when adding a favorite place, but you will probably need to do something like this:

Vue.set(this.singlePlaces, indexOfItem, updatedItem)

Solution 2:

First of all, instead of your markup you could save two times less events handling, two times less DOM nodes creation and two times less recompute and redraw just by toggling the class. Also I'm not sure if invoking a function inside @click event wouldn't fire this function immediately and would just in case wrap it into anonymous function. Also, notice that instead of place.id I am passing whole place object reference to addFavoritePlace:

<span
    @click="() => addFavoritePlace(place)"
    class="favorite-btn"
    :class="{active: place.is_favorited}">
        <i class="icon-heart"></i>
</span>

Second of all, part of filteredPlaces code does not have sense as you are setting variable that is not used elsewhere nor returned, so garbage collector will kill it immediately anyway. Instead you could just go with:

computed: {
  filteredPlaces () {
    const search = this.search.toLowerCase()
    return this.singlePlaces.filter((p) => {
        return p.name.toLowerCase().includes(search)
    });
  }
}

And at last, you can just update singlePlaces items and they will automatically affect filteredPlaces, no need to do anything special, in case you don't have is_favorited property in every place you can map it on init or force Vue to track its updates with set method. Also, as I said, I would pass whole place reference instead of id so we wouldn't have to find this element again in the stack:

methods: {
  async addFavoritePlace (place) {
    await axios.post('/api/add-favorite-place', { place_id: place.id })
    this.$set(place, 'is_favorited', true)
  },
},

Solution 3:

If you want to force a computed property to update and recalculate it's value, you can simply use a data property and just mention that property in the computed function (you don't have to use it, just being there is enough), and then change that data property; This will force the computed value to update.

Here is the code:

data() {
  return {
    refreshKey: false,
    // ...
  }
},
computed: {
  myComputedValue() {
    // just mentioning refreshKey
    this.refreshKey;

    // rest of the function that actualy do the calculations and returns a value
  },
},
methods: {
  myMethod() {
    // the next line would force myComputedValue to update
    this.refreshKey = !this.refreshKey;

    // the rest of my method
  },
},