Jacob Hyde
Jacob Hyde

Reputation: 1000

You may have an infinite update loop in a component render function even with spread operator

I know this has been answered before, but after looking at "You may have an infinite update loop in a component render function" warning in Vue component I found the solution did not work.

Can someone help me figure out what is going on here?

<template>
  <div>
    <template v-for="listing in sortedListings">
      <listing
        :key="listing.uuid"
        :listing="listing"
        :highlight-color="listing.highlight ? getHighlightColor() : null"
      />
    </template>
  </div>
</template>

<script>
import Listing from '@/components/Listing'
export default {
  components: {
    Listing,
  },
  data: function () {
    return {
      highlightColors: this.getHighlightColors(),
      listings: [
        {
          uuid: '658f325f-33c8-455b-98f6-27eb4eaa16a0',
          title: 'Cursus Nullam Amet Tortor',
          location: 'Remote',
          url: 'http://example.net/birds',
          hours_week: 20,
          tags: ['django', 'python', 'flask'],
          logo: 'https://logo.clearbit.com/apple.com',
          company_name: 'Apple',
          show_logo: 1,
          highlight: 0,
          stick_top: 0,
        },
        {
          uuid: '658f325f-33c8-455b-98f6-27eb4eaa16a1',
          title: 'Donec id elit non mi porta gravida at eget metus',
          location: 'Remote',
          url: 'http://example.net/birds',
          hours_week: 20,
          tags: ['django', 'python', 'flask', 'full-stack', 'contract'],
          logo: 'https://logo.clearbit.com/apple.com',
          company_name: 'Mapple',
          show_logo: 0,
          highlight: 0,
          stick_top: 0,
        },
        {
          uuid: '658f325f-33c8-455b-98f6-27eb4eaa16a2',
          title:
            'Donec ullamcorper nulla non metus auctor fringilla ullamcorper dapibus',
          location: 'Remote / USA',
          url: 'http://example.net/birds',
          hours_week: 20,
          tags: ['django', 'python', 'flask', 'full-stack', 'vue.js'],
          logo: 'https://logo.clearbit.com/apple.com',
          company_name: 'Fapple',
          show_logo: 1,
          highlight: 0,
          stick_top: 1,
        },
        {
          uuid: '658f325f-33c8-455b-98f6-27eb4eaa16a3',
          title: 'Tristique Euismod Venenatis Porta',
          location: 'San Francisco, CA, USA',
          url: 'http://example.net/birds',
          hours_week: 20,
          tags: ['django', 'python', 'flask', 'full-stack', 'vue.js'],
          logo: 'https://logo.clearbit.com/apple.com',
          company_name: 'Lapple',
          show_logo: 1,
          highlight: 1,
          stick_top: 0,
        },
        {
          uuid: '658f325f-33c8-455b-98f6-27eb4eaa16a4',
          title: 'Tristique Euismod Venenatis',
          location: 'San Francisco, CA, USA',
          url: 'http://example.net/birds',
          hours_week: 20,
          tags: ['django', 'python', 'flask', 'full-stack', 'vue.js'],
          logo: 'https://logo.clearbit.com/apple.com',
          company_name: 'Dapple',
          show_logo: 1,
          highlight: 1,
          stick_top: 1,
        },
      ],
    }
  },
  computed: {
    sortedListings: function () {
      return [...this.listings].sort(function (a, b) {
        return b.stick_top - a.stick_top
      })
    },
  },
  methods: {
    getListings: async function () {},
    getHighlightColors: function () {
      return this.shuffleArray([
        '#E3F2FD',
        '#E8EAF6',
        '#FFEBEE',
        '#E0F2F1',
        '#E8F5E9',
        '#FFF3E0',
        '#FFFDE7',
      ])
    },
    getHighlightColor: function () {
      if (this.highlightColors.length === 0) {
        this.highlightColors = this.getHighlightColors()
      }
      return this.highlightColors.shift()
    },
  },
  mounted: function () {
    this.getListings()
  },
}
</script>

Within the computed property sortedListings I am already doing [...this.listings]

Upvotes: 1

Views: 334

Answers (2)

Dan
Dan

Reputation: 63089

getHighlightColor() is causing the infinite loop.

This is a classic illustration of why it can be bad (not always) to bind to a method in the template. If your method changes a model, Vue has to rerender the component. Which then triggers the method and changes the model again, which then causes a rerender of the component, and so on.

This demo will produce the same result but not if you use the computed in the template instead:

new Vue({
  el: "#app",
  data() {
    return {
      arr: [1,2,3,4,5]
    }
  },
  methods: {
    change: function () {
      return this.arr.shift();
    },
  },
  computed: {
    test() {
      return this.arr.shift();
    }
  }
});
<div id="app">
  <div>
    {{ change() }}
  </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

Upvotes: 2

Boussadjra Brahim
Boussadjra Brahim

Reputation: 1

the spread operator doesn't deep clone the array and the sort method modify the original property which is used in computed that generate an infinite loop, so add a method to clone the object deeply like this one :

...

 computed: {
    sortedListings: function () {
      return this.deepCopy(this.listings).sort(function (a, b) {
        return b.stick_top - a.stick_top
      })
    },
  },
  methods: {
   deepCopy(src) {
     let target = Array.isArray(src) ? [] : {};
     for (let prop in src) {
      let value = src[prop];
       if(value && typeof value === 'object') {
        target[prop] = deepCopy(value);
       } else {
        target[prop] = value;
      }
   }
     return target;
  }
  ,
    getListings: async function () {},
...

Upvotes: 2

Related Questions