'Vue 3 how to rerender properly when prop value is updated

I have a Vue component to display a list of data. It should receive the data and sortBy as props and render correctly. However, I have an extra button in the component that I want to update sortBy and re-render the new list but I don't know how to assign new data to computed property sortedData. Thanks a lot if I can have some advices.

<template>
<div>
 <template v-for="(item, index) in sortedData" :key="index">
                  {{ item.name }}
 </template>
 <button @click.prevent="sortWith('color')">sort Color</button>
</div>
</template>

<script>
export default {
props: {
    data: {
      type: Array,
      default: []
    },
    sortBy: {
      type: String,
      default: 'name'
    },
  },
methods: {
 sort(array, sortBy) {
      return array.sort(function (a, b) {
        return b[sortBy] - a[sortBy]
      })
 },
sortWith(sortBy) {
  // Need to sort the list and re-render new order
}
}
computed: {
    sortedData() {
      return this.sort(this.data, this.sortBy)
    }
  }
}
</script>


Solution 1:[1]

Try like following snippet:

const app = Vue.createApp({
  data() {
    return {
      items: [{name: 'aaa', color: 'red'}, {name: 'ccc', color: 'purple'}, {name: 'fff', color: 'yellow'}, {name: 'bbb', color: 'blue'}, {name: 'eee', color: 'green'}],
      sortingBy: 'name',
      sortingOrder: false
    }
  },
  methods: {
    setSorting(item) {
      this.sortingBy = item
      this.sortingOrder = !this.sortingOrder
    }
  }
})

app.component('child', {
  template: `
    <div>
      <template v-for="(item, index) in sortedData" :key="index">
        <div>{{ item.name }} -  {{ item.color }}</div>      
      </template>
      <div class="sort">
        <p>Sort By (child) :</p>
        <div v-for="(title, i) in Object.keys(data[0])" :key="i" class="fields">
          <a href="#" @click="sorting(title)">{{ title }}</a>
        </div>
      </div>
    </div>
  `,
  props: {
    data: {
      type: Array,
      default: []
    },
    sortBy: {
      type: String,
      default: 'name'
    },
    sortOr: {
      type: String,
      default: false
    },
  },
  data() {
    return {
      sortField: this.sortBy,
      sortOrder: this.sortOr
    }
  },
  methods: {
    sort(array, sortBy) {
      return array.sort((a, b) => {
        let x = a[sortBy].toLowerCase()
        let y = b[sortBy].toLowerCase()
        if (this.sortOrder) {
          return x > y ? -1 : 1;
        } else {
          return x > y ? 1 : -1;
        }
        return 0
      })
    },
    sorting(item) {
      this.sortField = item
      this.sortOrder = !this.sortOrder
    }
  },
  computed: {
    sortedData() {
      return this.sort(this.data, this.sortField) 
    }
  },
  watch: {
    sortBy: function (val) {
      this.sortField = val
    },
    sortOr: function (val) {
      this.sortOrder = val
    },
  }
})
app.mount('#demo')
.sort {
  display: flex;
  align-items: center;
}
.fields {
  margin-left: 1em;
}
<script src="https://unpkg.com/[email protected]/dist/vue.global.prod.js"></script>
<div id="demo">
  <div class="sort">
    <p>Sort By (parent) :</p>
    <div v-for="(title, i) in Object.keys(items[0])" :key="i" class="fields">
      <a href="#" @click="setSorting(title)">{{ title }}</a>
    </div>
  </div>
  <child :data="items" :sort-by="sortingBy" :sort-or="sortingOrder"></child>
</div>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1