Vue: All components rerender upon unrelated data property change only if a prop comes from a method that returns an object or array

Viewed 29

(Vue 3, options API)

The problem: Components rerender when they shouldn't.

The situation:

  1. Components are called with a prop whose value comes from a method.
  2. The method cannot be replaced with a computed property because we must make operations on the specific item (in a v-for) that will send the value processed for that component.
  3. The method returns an Array. If it returned a primitive such as a String, components wouldn't rerender.

To reproduce: change any parent's data property unrelated to the components (such as showMenu in the example below).

Parent

<template>
  <div>
    <div id="menu">
      <div @click="showMenu = !showMenu">Click Me</div>
      <div v-if="showMenu">
        Open Console: A change in a property shouldn't rerender child components if they are not within the props. But it does because we call myMethod(chart) within the v-for, and that method returns an array/object. 
      </div>
    </div>
    <div v-for="(chart, index) in items" :key="index">
      <MyComponent :table="myMethod(chart)" :title="chart.title" />
    </div>
  </div>
</template>
<script>
import MyComponent from './MyComponent.vue';
export default {
  components: {
    MyComponent,
  },
  data: function () {
    return {
      showMenu: false,
      items: [{ value: 1 }, { value: 2 }],
    };
  },
  methods: {
    myMethod(item) {
      // Remove [brackets] and it doesn't rerender all children
      return ['processed' + item.value];
    }
  }
};
</script>

Child

<template>
  <div class="myComponent">
    {{ table }}
  </div>
</template>
<script>
export default {
  props: ['table'],
  beforeUpdate() {
    console.log('I have been rerendered');
  },
};
</script>
<style>
.myComponent {
  width: 10em;
  height: 4em;
  border: solid 2px darkblue;
}
</style>

Here's a Stackblitz that reproduces it https://stackblitz.com/edit/r3gg3v-ocvbkh?file=src/MyComponent.vue

I need components not to rerender. And I don't see why they do.

Thank you!

1 Answers

To avoid this unnecessary rerendering which is the default behavior try to use v-memo directive to rerender the child component unless the items property changes :

<div v-for="(chart, index) in items" :key="index" v-memo="[items]">
   <MyComponent :table="myMethod(chart)" :title="chart.title" />
 </div>
Related