Back to Posts

Use Methods and $forceUpdate instead of Computed Variables When Altering Child to Reflect in DOM

Has this ever happened to you? You are trying to use computed to sort and array of objects for visibility in a `v-for` loop only to have updates to those objects not be reflected in the computed variable!? GAH! This one can be so frustrating.

⁠⁠When the values of an Object in Vue 2 are updated it does not automatically trigger a UI update.

⁠There are a couple ways to go about fixing this problem, and I am going to go about giving you the hacky, less extra variables solution: `$forceUpdate`

Example:

<template>
  <ol>
    <li v-for="(item, index) in nonDeletedItems" :key="index">
      {{ item.title }} 
      <button type="button" @click="handleDelete(item)">Delete</button>
    </li>
  </ol>
</template>

<script>
export default {
  data: {
    items: [
      {
        title: 'Title 1',
        deleted: false
      },
      {
        title: 'Title 2',
        deleted: false
      },
      {
        title: 'Title 3',
        deleted: false
      }
    ]
  },
  computed: {
    nonDeletedItems() {
      return this.items.filter((i) => !i.deleted)
    }
  },
  methods: {
    handleDelete(item) {
      item.deleted = true
    }
  }
}
</script>

In this component, we list items and display their title. We also have a "soft delete" meaning we want to omit deleted items from the view but we don't want to actually remove them from the original array.

A solution to this problem involves using a method instead of a computed property for the `nonDeletedItems` array.

Solution:

<template>
  <ol>
    <li v-for="(item, index) in getNonDeletedItems" :key="index">
      {{ item.title }} 
      <button type="button" @click="handleDelete(item)">Delete</button>
    </li>
  </ol>
</template>

<script>
export default {
  data: {
    items: [
      {
        title: 'Title 1',
        deleted: false
      },
      {
        title: 'Title 2',
        deleted: false
      },
      {
        title: 'Title 3',
        deleted: false
      }
    ]
  },
  methods: {
    handleDelete(item) {
      item.deleted = true
      this.$forceUpdate()
    },
    getNonDeletedItems() {
      return this.items.filter((i) => !i.deleted)
    }
  }
}
</script>

In addition to the method, `getNonDeletedItems()`, we must call `this.$forceUpdate()` where we are modifying the child property of an items object.

If we don't add the force update, we will have the same issue as we do with computed properties where you won't see the change until another lifecycle happens.

© 2024