0
votes

I have two components with parent-child relations. The parent component can contain any number of child components using a v-for loop.

Essentially the child component contains a bootstrap table b-table and the parent component is just a wrapper around the child component. Therefore, the parent component can have any number of tables inside it.

The tables have the functionality to check or uncheck rows of the table. I want to keep track of all the selected rows of all the tables inside the parent component. I am using a Vuex store to keep the id of all the selected rows from all the tables. Each child component commits to the vuex store after any rows is checked/unchecked. It all works all fine till here. Using the vue devtools I can confirm that the vuex store always has the correct data in it. (the vuex state property name is selectedObjects, it is an array of strings).

Now, I want to display some data on the parent component based of the vuex store value (using v-show). I want to show the data only when selectedObjects in the store is not an empty array.

I have a getter to get the selectedObjects state from the store, and I use this getter in the parent component as a computed property using mapGetters.

The problem is that everytime a child component makes any change to the vuex store, the parent component refreshes the prop that is passed on to the child components. The prop being passed is not dependant on the vuex store at all.

<template>
  <div>
    <div v-if="isDataLoaded">
      <div>
        <div>
          <div>
            <span v-show="showBulkActions"> Action1 </span>
            <span v-show="showBulkActions"> Action2 </span>
            <span v-show="showBulkActions"> Action3 </span>
            <span v-show="showBulkActions">Action4</span>
            <span> Action5 </span>
          </div>
        </div>
      </div>
      <div>
        <template v-for="objectId in objectIds">
          <ObjectList
            :key="objectId"
            :objects="getObjectsFor(objectId)"
            :payload="payload"
          ></ObjectList>
        </template>
      </div>
    </div>    
  </div>
</template>

<script>

export default {
  props: {
    payload: Object,
    isDataLoaded: false
  },
  data() {
    return {
            objectIds: [],
            objects: []
    };
  },
  computed: {
    ...mapGetters(["getSelectedObjects"]),
    showBulkActions() {
            // return true; --> works fine

            //This does not work as expected
      const selectedObjects = this.getSelectedObjects;
      return selectedObjects.length > 0;
    }
    },

  mounted: async function() {
    // init this.objects here
    },

  methods: {
    ...mapGetters(["getObjects"]),
    getObjectsFor(objectId) {
            //this line gets executed everytime the selectedObjects is modified in vuex store.
            //this method is only being called from one place (prop of ObjectList component)
      return this.objects.filter(f => f.objectId === objectId);
        }
  }
};
</script>

According to my understanding, the getObjectsFor method should not be called when the selectedObjects array in vuex store is changed, because it does not depend on it.

Why is this happening ?

1

1 Answers

1
votes

Your parent component template depends on selectedObjects Vuex store value (through showBulkActions computed prop and getSelectedObjects getter as computed prop).

Every time selectedObjects changes in store, update (and re-render) of parent component is triggered.

And as stated in Vue documentation:

every time the parent component is updated, all props in the child component will be refreshed with the latest value

This means expressions used to populate child components props (call to getObjectsFor method in your case) needs to be evaluated. That's why your method is called.

Solution would be to pass all objects to your child components as a prop and do the filtering (done in getObjectsFor method) inside your child component as computed prop...