Home > database >  Vue - state is updating for all array items
Vue - state is updating for all array items

Time:02-20

i have a very simple app that increment counter value for each array item individually. the problem is when i am incrementing the counter value, it updates for all array items

i know that it's possible to update counter value with each index of the array but can't find a better way to do it

<script setup>
const counter = ref(0)

const increment = () => {
  counter.value = counter.value   1
}

const data = [{ name: 'john' }, { name: 'paul' }]
</script>

<template>
  <div v-for="item in data" :key="item.name">
    <div style="display: flex; gap:2rem">
      <span>Name: {{ item.name }}</span>
      <span>Age:{{ counter }}</span>
      <button @click="increment">
        increment
      </button>
    </div>
  </div>
</template>

CodePudding user response:

Now you have only one counter. I understand that you want individual counter for each person.

Our people are:

 1. { name: 'john' }
 2. { name: 'paul' }

If we want to have individual counter, we need to add counter for individual person (with default state, so counter is equal to 0):

 1. { name: 'john', counter: 0 }
 2. { name: 'paul', counter: 0 }

Now we can remove our single counter and increment function:

const counter = ref(0)
const increment = () => {
  counter.value = counter.value   1
}

Next, change displayed counter (to handle our new individual counter) from:

<span>Age:{{ counter }}</span>

to:

<span>Age:{{ item.counter }}</span>

And our click function should increment person's individual counter so:

<button @click="item.counter  ">
  increment
</button>

The last thing is wrapping our data variable in ref. The same you did with single counter. We have to share state between rerenders:

const data = ref([
  { name: 'john', counter: 0 },
  { name: 'paul', counter: 0 },
]);

At the end our code should look like this:

<script setup>
import { ref, computed } from 'vue';

const data = ref([
  { name: 'john', counter: 0 },
  { name: 'paul', counter: 0 },
]);
</script>

<template>
  <div v-for="item in data" :key="item.name">
    <div style="display: flex; gap: 2rem">
      <span>Name: {{ item.name }}</span>
      <span>Age:{{ item.counter }}</span>
      <button @click="item.counter  ">increment</button>
    </div>
  </div>
</template>

And there is a stackblitz: https://stackblitz.com/edit/vitejs-vite-xthp6v?file=src/components/HelloWorld.vue

CodePudding user response:

To achieve the requirement, you have to bind the counter property in each object. So that you can play with it individualy.

Working Demo :

new Vue({
  el: '#app',
  data: {
    data: [{ name: 'john', counter: 0 }, { name: 'paul', counter: 0 }],
  },
  methods: {
    increment(index) {
        this.data[index].counter = this.data[index].counter   1 
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div v-for="(item, index) in data" :key="index">
    <div style="display: flex; gap:2rem">
      <span>Name: {{ item.name }}</span>
      <span>Age: {{ item.counter }}</span>
      <button v-on:click="increment(index)">
        increment
      </button>
    </div>
  </div>
</div>

  • Related