Ad

Vue - State Is Updating For All Array Items

- 1 answer

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>
Ad

Answer

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%2Fcomponents%2FHelloWorld.vue

Ad
source: stackoverflow.com
Ad