How to Stop an Infinite Loop For - Vue

Asked

Viewed 131 times

1

I’m doing a project with Vuejs and I have these two functions running through an array provided by a JSON fetch.

import { api, getCep } from "@/services.js";
export default {
  name: 'Casos',
  data() {
    return {
      //cep: '',
      //bairro: '',
      bairros: '',
      casoPorBairro: '',
      casos: null,
      valores: '',
      totalCasos: '',
      totalObitos: '',
      changeView: false,
      listBairros: [],
      searchBairro: '',
      searchCasos: '',
      searchObitos: '',
    };
  },
  methods: {
    buscarCep() {
      const cep = this.cep.replace(/\D/g, "");
      if(cep.length === 8) {
        getCep(cep).then(r => {
          this.bairro = r.data.bairro;
        })
      }
    },
    getCasos() {
      api.get(`/covid19.min.json`).then(r => {
        this.casos = r.data.bairros; // => PRODUCTION
        //this.casos = r.data; // => DEVELOPMENT
      });
    },
    showBairro() {
      for(let i = 0; i < this.casos.length; i++){
        this.listBairros.push(this.casos[i]);
      }
      const result = this.listBairros.find(list => list.bairro === this.bairros);
      if(result) {
        this.searchBairro = result.bairro;
        this.searchCasos = result.casos;
        this.searchObitos = result.obitos
      }
    },
    getValorMax() {
      let sumCasos = 0;
      let sumObitos = 0;
      for(let i = 0; i < this.casos.length; i++){
        this.valores = this.casos[i].casos;
        sumCasos = sumCasos + this.casos[i].casos;
        sumObitos = sumObitos + this.casos[i].obitos;
      }
      this.totalCasos = sumCasos.toLocaleString();
      this.totalObitos = sumObitos.toLocaleString();
    },
    changeColor() {
      const btnList = document.querySelector('button[data-btn="list"]');
      const btnTable = document.querySelector('button[data-btn="table"]');
      if(this.changeView === false) {
        btnList.classList.remove('active');
        btnTable.classList.add('active');
      } else {
        btnList.classList.add('active');
        btnTable.classList.remove('active');
      }
    }
  },
  created() {
    this.getCasos();
  },
  beforeUpdate() {
    this.getValorMax();
    this.getCasos();
    this.showBairro();
  },
  updated() {
    this.changeColor();
  }
}

As I need to wait for my fetch to occur to insert the json data into the array, both functions are being called in the method beforeUpdate() of the Vue. However, the for loop of both functions are generating an infinite loop.

Follows full file: https://github.com/GuiiHenriq/sp-covid19/blob/master/src/components/Casos.vue

How can I fix this?

  • Clarifies 2 things: 1) why you call this.getCasos(); in beforeUpdate and not only 1 time when the component loads? 2) the data source over which everything else is calculated comes from that this.getCasos(); right?

  • Sergio, your comment helped me clarify the problem, really the infinite loop is being caused because getCasos() was being called in beforeUpdate too, the correct is to call only when creating the component.

  • If you answer my questions you have a one-answer bonus telling you how you can do it in a better way ;)

    1. As I explained was a mistake, I am currently calling the this.getCasos() in the method created(). 2) That exact, the this.getCasos() is where I have my Axios get function. The two other functions this.showBairro() and this.getValorMax() are using the this.getCasos().

1 answer

3


Since all that this component needs comes from data that this.getCasos() was then seek them (or the values they generate) must by definition be computed properties, thus creating a reactive flow. As you have (the this.getCasos() to be called in each beforeUpdate) creates an infinite loop because when this.casos receive these new values the beforeUpdate is called again, and so on, cyclically.

The logic is to create all other content when this.casos change and without having to call methods manually.

The way to do it would be like this:

new Vue({
  name: "Casos",
  el: '#app',
  data() {
    return {
      //cep: '',
      //bairro: '',
      bairros: "",
      casoPorBairro: "",
      casos: [],
      changeView: false
    };
  },
  computed: {
    searchBairro() {
      const searchString = this.bairros.trim();
      return (
        searchString &&
        this.casos.find(({
          bairro
        }) => bairro === this.bairros)
      );
    },
    totais() {
      let sumCasos = 0;
      let sumObitos = 0;
      for (let i = 0; i < this.casos.length; i++) {
        sumCasos = sumCasos + this.casos[i].casos;
        sumObitos = sumObitos + this.casos[i].obitos;
      }

      return {
        casos: sumCasos.toLocaleString(),
        obitos: sumObitos.toLocaleString()
      };
    }
  },
  methods: {
    buscarCep() {
      const cep = this.cep.replace(/\D/g, "");
      if (cep.length === 8) {
        getCep(cep).then(r => {
          this.bairro = r.data.bairro;
        });
      }
    },
    getCasos() {
      // exemplo estático:
      const url = 'https://raw.githubusercontent.com/GuiiHenriq/sp-covid19/master/public/covid19.min.json';

      fetch(url)
        .then(response => response.json())
        .then(r => {
          this.casos = r.bairros;
        });
    },
    changeColor() {
      const btnList = document.querySelector('button[data-btn="list"]');
      const btnTable = document.querySelector('button[data-btn="table"]');
      if (this.changeView === false) {
        btnList.classList.remove("active");
        btnTable.classList.add("active");
      } else {
        btnList.classList.add("active");
        btnTable.classList.remove("active");
      }
    }
  },
  created() {
    this.getCasos();
  },
  updated() {
    this.changeColor();
  }
});
.wrapper {
  max-width: 960px;
  margin-right: auto;
  margin-left: auto;
  padding-right: 10px;
  padding-left: 10px;
}

.casos,
.infos {
  margin-top: 20px;
}

h2 {
  margin-bottom: 12px;
  color: #706fd3
}

.casos-content,
.infos-content {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
}

.infos-content {
  justify-content: space-between;
}

.infos div {
  margin-bottom: 20px;
  padding: 22px 12px;
  width: 215px;
  border-radius: 4px;
  background: #fff;
  border: 1px solid #dbe9f5;
  box-shadow: 0 4px 6px 0 rgba(31, 70, 88, .04);
}

.infos div h1 {
  margin-right: 12px;
  font-weight: 700;
  font-size: 40px;
  line-height: 48px;
  letter-spacing: -1.29px;
}

.infos div p {
  font-weight: bold;
  font-size: 18px;
}

.search {
  width: 100%;
  margin: 30px 0 60px 0;
}

.search section {
  padding: 22px 0 22px 12px;
  width: 100%;
  border-radius: 4px;
  background: #fff;
  border: 1px solid #dbe9f5;
  box-shadow: 0 4px 6px 0 rgba(31, 70, 88, .04);
  font-size: 18px;
  letter-spacing: .5px;
}

.search section p span {
  font-weight: bold;
}

.casos section {
  display: flex;
  justify-content: space-between;
}

.casos i {
  font-size: 32px;
  cursor: pointer;
  color: #345;
}

.casos button.active i {
  color: #706fd3;
}


/* Table/Cards */

.casos-content li {
  margin-bottom: 20px;
  padding: 22px 0 22px 12px;
  width: 215px;
  border-radius: 4px;
  background: #fff;
  border: 1px solid #dbe9f5;
  box-shadow: 0 4px 6px 0 rgba(31, 70, 88, .04);
  font-size: 18px;
  letter-spacing: 0.5px;
}

.casos li:last-child {
  width: 100%;
  text-align: center;
  margin-bottom: 60px;
}

.casos li span {
  font-weight: bold;
}


/* List */

.casos-content-list {
  max-height: 600px;
  overflow: auto;
  margin-bottom: 60px;
}

.casos-content-list li:last-child {
  margin: 0;
}

.list-head {
  display: flex;
  padding: 10px;
  margin-bottom: 15px;
  background: #fff;
  box-shadow: 0 4px 6px 0 rgba(31, 70, 88, .04);
}

.list-head li {
  display: flex;
  justify-content: space-around;
  flex-direction: row;
  width: 100%;
  margin: 0 !important;
  font-size: 20px;
}

.list-infos {
  width: 100%;
  border-radius: 0;
  margin: 0;
  padding: 10px;
  box-shadow: 0 4px 6px 0 rgba(31, 70, 88, .04);
  background: #fff;
  font-size: 18px;
  letter-spacing: 0.5px;
  display: flex;
  justify-content: space-around;
}

.list-infos:nth-child(odd) {
  background: #f7f7f7;
}

.casos-content-list .list-infos p {
  width: 100%;
  text-align: center;
}

#busca {
  width: 100%;
  padding: 20px;
  border: none;
}

#busca:focus,
#busca:hover {
  transform: scale(1.1);
}

#lupa {
  width: 62px;
  height: 62px;
  background: url("../assets/search.svg") no-repeat center center;
  text-indent: -150px;
  border: none;
  cursor: pointer;
  position: absolute;
  top: 0px;
  right: 0px;
  box-shadow: none;
}

@media only screen and (max-width: 768px) {
  .infos div,
  .casos li {
    width: 100%;
  }
  .casos li:last-child {
    text-align: left;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>

<main id="app">
  <div class="wrapper">
    <div class="infos">
      <h2>Dados Gerais</h2>
      <section class="infos-content">
        <div>
          <p>Casos Confirmados</p>
          <h1>{{ totais.casos }}</h1>
        </div>
        <div>
          <p>Óbitos</p>
          <h1>{{ totais.obitos }}</h1>
        </div>
      </section>
    </div>

    <main class="search">
      <h2>Procure seu bairro</h2>
      <form>
        <!--<input id="cep" name="cep" type="text" placeholder="Bela Vista" v-model="cep" @keyup="buscarCep">-->
        <input id="cep" name="cep" type="text" placeholder="Bela Vista" v-model="bairros" />
      </form>

      <section v-if="searchBairro">
        <p><span>Bairro:</span> {{ searchBairro.bairro }}</p>
        <p><span>Casos:</span> {{ searchBairro.casos }}</p>
        <p><span>Óbitos:</span> {{ searchBairro.obitos }}</p>
      </section>
    </main>

    <main class="casos">
      <section>
        <div>
          <h2>Todos os casos</h2>
        </div>
        <div>
          <button @click="changeView = false" data-btn="table" class="active">
              <i class="fas fa-table"></i>
            </button>
          <button @click="changeView = true" data-btn="list">
              <i class="fas fa-list-alt"></i>
            </button>
        </div>
      </section>

      <ul class="casos-content" v-if="changeView === false">
        <li v-for="caso in casos" :key="caso.bairro">
          <p><span>Bairro:</span> {{ caso.bairro }}</p>
          <p><span>Casos:</span> {{ caso.casos }}</p>
          <p><span>Óbitos:</span> {{ caso.obitos }}</p>
        </li>
      </ul>
      <ul class="casos-content-list" v-else>
        <div class="list-head">
          <li>
            <p><span>Bairro</span></p>
          </li>
          <li>
            <p><span>Casos</span></p>
          </li>
          <li>
            <p><span>Óbitos</span></p>
          </li>
        </div>
        <li v-for="caso in casos" :key="caso.bairro" class="list-infos">
          <p>{{ caso.bairro }}</p>
          <p>{{ caso.casos }}</p>
          <p>{{ caso.obitos }}</p>
        </li>
      </ul>
    </main>
  </div>
  </div>
</main>


PS: sent a PR if you want to use this version I suggested here.

  • 1

    I understood, I did not remember the use of computed properties, but with its explanation I could understand. I accepted your PR and merged it with the master, did the tests locally and in production, worked perfectly. Thanks for your explanation was of great help and thanks for contributing to the project as well. Thank you very much!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.