Search code examples
vue.jsvuejs2lifecyclevue-component

VueJs doesn't update when binding passed props to data


I'm a beginner in vue. I was wondering why the lifecycle of vue doesn't trigger when the value updates.

Vue.component('list-table', {
  props: ['data'],
  template: `
    <div>
      {{ mydata }}
    </div>
  `,
  beforeUpdate () { console.log('before update') },
  updated () { console.log('updated') },
  data () {
    return {
      mydata: this.data
    }
  }
})

const app = new Vue({
  el: '#app',
  data: {
    datum: [
      { f: 'John', l: 'Doe', g: 'Male' },
      { f: 'Jane', l: 'Doe', g: 'Female' },
      { f: 'John', l: 'Smith', g: 'Male' },
      { f: 'Jane', l: 'Smith', g: 'Female' },
      { f: 'Roy', l: 'Smith', g: 'Male' },
      { f: 'Reign', l: 'Smith', g: 'Female' }
    ],
    itemPerPage: 2,
    currentPage: 0
  },
  computed: {
    filteredList () {
      const startIdx = this.currentPage * this.itemPerPage
      const endIdx = startIdx + this.itemPerPage 
      return this.datum.slice(startIdx, endIdx)
    },
    totalPages () {
      return Math.ceil(this.datum.length / this.itemPerPage)
    }
  },
  methods: {
    setPage (num) {
      this.currentPage = num - 1
    }
  }
})

I know that the binding won't change because it was instantiated on passing only but if you see the updated function it won't trigger the log. Here's a pen to check it.


Solution

  • You are initializing your component's local data to what is in the prop, but are not otherwise using the prop, so no updating ever happens. If you change the component to use data instead of mydata, updating happens.

    If you want to make some change based on updated data, use a watch on the data.

    Vue.component('list-table', {
      props: ['data'],
      template: `
        <div>
          {{ data }}
        </div>
      `,
      beforeUpdate () { console.log('before update') },
      updated () { console.log('updated') },
      data () {
        return {
          mydata: this.data
        }
      }
    })
    
    const app = new Vue({
      el: '#app',
      data: {
        datum: [
          { f: 'John', l: 'Doe', g: 'Male' },
          { f: 'Jane', l: 'Doe', g: 'Female' },
          { f: 'John', l: 'Smith', g: 'Male' },
          { f: 'Jane', l: 'Smith', g: 'Female' },
          { f: 'Roy', l: 'Smith', g: 'Male' },
          { f: 'Reign', l: 'Smith', g: 'Female' }
        ],
        itemPerPage: 2,
        currentPage: 0
      },
      computed: {
        filteredList () {
          const startIdx = this.currentPage * this.itemPerPage
          const endIdx = startIdx + this.itemPerPage 
          return this.datum.slice(startIdx, endIdx)
        },
        totalPages () {
          return Math.ceil(this.datum.length / this.itemPerPage)
        }
      },
      methods: {
        setPage (num) {
          this.currentPage = num - 1
        }
      }
    })
    <script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.min.js"></script>
    <div id='app'>
      <table>
        <thead>
          <tr>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Gender</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for='list in filteredList'>
            <td v-for='prop in list'>
              <list-table :data='prop' />
            </td>
          </tr>
        </tbody>
      </table>
      <div>
        <span v-for='page in totalPages'>
          <button @click='setPage(page)'>{{ page }}</button>
        </span>
      </div>
    </div>