Search code examples
javascripthtmlvue.jsvue-componentbootstrap-vue

b-table reloads nested components when "unshifting" a new row (but not when "pushing")


I have a vue component which contains this table, which also has a component inside its only row:

    <template>
        <b-table :items="records">
          <template slot="row-details">
              <child-component/>
          </template>
        </b-table>
    </template>

I'm keeping the data inside the table very simple, for sample purposes:

    data() {
       return {
         records: [{
           name: "Parent Row",
           _showDetails: true
         }]
       };
     }

The child-component inside the row is quite simple too:

<template>
<div>
    <p>{{number}}</p>
</div>
</template>

<script>
export default {
data() {
    return {
      number: Math.random(),
      isUpdating: console.log("Updating Child Component")
    };
  },
}
</script>

If I add a new row in the parent table using records.push(newRow) everything works fine, I can see the new row and, in the child component, number does not change.

BUT If I add the new row using records.unshift(newRow) the child component is reloaded, the "Updating child component" message shows and number changes every time.

Is this the expected behaviour? How can I keep the number so it does not change when I unshift a new record?

I have created a working sample here.


Solution

  • To minimize re-renders of child components in a b-table, and if your data has a field that is unique for every row (i.e. an ID or primary key), set the primary-key prop to the name of the field that contains the unique row ID. This unique ID will be used as the Vue key for each <tr> element. Vue will then know if it needs to re-render the children or not.

    Otherwise, what is happening is that b-table uses the row's index as the key. By pushing new rows on the table, the previous rows' Vue key stays the same (and hence not re-rendered), but if you shift on the top of the rows, those that previously has indexes of 0, 1, 2, etc have been replaced by new rows... and the entire table and it's contents (children) must be re-rendered.

    EDIT/UPDATE:

    I've discovered the root cause. The row-details slot was always using the row's index as its :key, and not incorporating the primary key value (if available). PR https://github.com/bootstrap-vue/bootstrap-vue/pull/4025 will fix this issue and will be available in the 2.0.0 stable release (being released today hopefully).

    UPDATE 2:

    BootstrapVue v2.0.0 stable has just been released (2019-09-06)