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 ?
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...