Incrementing a piece of state causes multiple re-render loop. Assigning state works fine, why does this happen?

Viewed 32

Baasically, I have a REST Api which returns a list of components (sections) from a CMS and their associated data. The UI has a little number next to each section to show the section number, BUT, some sections do not count. For e.g, a career block section. This number is passed as a prop to the rendered section in the form of :number=""

So, my function loops through the function and if the component is in the allowedNumerableSections array, it will increment, if it doesn't, it will return false and add a increment to the decrementAmount state

So basically, say we have 5 sections

Section1
Section2
Section3
Section4
Section5

Section3 is NOT in the allowedNumerableSections array, so when looping these out, so, if we were to loop out these sections and the passed :number prop for each section, it would read:

<Section1 number="1" />
<Section2 number="2" />
<Section3 number="false" />
<Section4 number="3" />
<Section5 number="4" />

So, we are basically skipping over Section3. So then the UI could read /01 for Section 1 and /03 for Section 4.

The problem, is once I add logic to increment state, it loops specifically 608 times for some reason, and the end number ends up saying /0-201 for example instead of /04, or /0-204 instead of /01

Here is my code:

<template>
  <div class="sections">
    <component
      :is="getSection(section.__component)"
      v-for="(section, index) in sections"
      :key="index"
      :section="section"
      :number="getNumber(section, index)"
      class="section"
    />
  </div>
</template>

<script>
export default {
  name: "SectionStructure",

  components: {
    HeroStyle1: () => import("~/components/hero/HeroStyle1.vue"),
    CommunityBlock: () => import("~/components/sections/CommunityBlock.vue"),
    FiftyFiftyMedia: () => import("~/components/sections/FiftyFiftyMedia.vue"),
    FullscreenGame: () => import("~/components/sections/FullscreenGame.vue"),
    GameRow: () => import("~/components/sections/GameRow.vue"),
    NewsBlock: () => import("~/components/sections/NewsBlock.vue"),
    TitleSideImage: () => import("~/components/sections/TitleSideImage.vue"),
    TwoImagesCta: () => import("~/components/sections/TwoImagesCTA.vue"),
    CareerList: () => import("~/components/sections/CareerList.vue"),
  },
  props: {
    sections: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      allowedNumerableSections: [
        "CommunityBlock",
        "FiftyFiftyMedia",
        "FullscreenGame",
        "GameRow",
        "NewsBlock",
        "TitleSideImage",
        "TwoImagesCta",
      ],
      decrementAmount: 0,
    };
  },
  methods: {
    getSection(str) {
      return str.substring(str.indexOf(".") + 1);
    },
    getNumber(section, index) {
      // If there is any decrementAmount from previous sections,
      // Reduce it from the index of the current section for UI purposes.
      index = index - this.decrementAmount;

      // Converting component names from my-component to MyComponent
      let component = this.getSection(section.__component);
      component = component.replace(/-([a-z])/g, function (g) {
        return g[1].toUpperCase();
      });
      component = component.charAt(0).toUpperCase() + component.slice(1);

      // If the section is a numbered section, return the number in the correct format
      if (this.allowedNumerableSections.includes(component)) {
        return index < 10 ? `/0${index}` : `/${index}`;
      }

      // If I INCREMENT the state, it causes a loop (specifically re-rendering 608 times in this case)
      // and the end number is not correct
      this.decrementAmount++;

      return false;
    },
  },
  computed: {},
};
</script>

Although, if I simply assign decrementAmount to a value, for e.g just writing this.decrementAmount = 3 it loops the correct number of times, but now all index's will be reduced by 3 which is what I do not want.

Anybody know why this keeps looping?

1 Answers

It looks like each re-render causes getNumber to re-calculate, in turn this updates number and everything keeps looping. Are you able to assign number when you receive the response but before first render? (e.g. on mount, but perhaps better if it's on the response handler)

mounted() {
  this.calculateNumber()
},
methods: {
  calculateNumber(apiResult) {
    let decrementAmount = 0
    apiResult.map(section => ({
      ...section,
      number: this.getNumber()
    }))
  }
}

As for why you get 608, that's most likely because you reached the recursion limit. How the limit is determined, I can't answer.

Related