Reputation: 1888
I have used vue.js for a couple of projects and I have been using the index as the key in the for loops
<div v-for="(item, index) in items" :key="index"></div>
...and have started to wonder if there are problems with that since examples usually use the ID of the item.
<div v-for="(item, index) in items" :key="item.ID"></div>
Upvotes: 42
Views: 27004
Reputation: 7127
From the Vue docs (emphasis mine): https://vuejs.org/guide/essentials/list.html#maintaining-state-with-key
To give Vue a hint so that it can track each node's identity, and thus reuse and reorder existing elements, you need to provide a unique key attribute for each item
If the index of any item in the array is changed (e.g. by adding/removing a new item anywhere other than the end of the array), then Vue will lose track of the item.
For example:
let data = [A, B, C, D]
<div v-for="(item, index) in data" :key="index">
Vue tracks each item like this:
A: 0
B: 1
C: 2
D: 3
If you remove B
from the array, Vue will then track each item like this:
A: 0
C: 1
D: 2
The indices of C
and D
have changed, so Vue has lost track of them, and will remove D
from the rendered output instead of B
(because from its point of view, it is the item with index 3 that was removed).
This is why the key should uniquely identify an item, which an index does not do.
However, it also means that there are some cases where using the index as the key is acceptable:
If you cannot use the index as a the key, but cannot uniquely identify items (e.g. some of the items in the list are identical), a solution may be to use lodash's uniqueId()
:
<div v-for="item in data" :key="uniqueId()">
or as mentioned here something like
<div v-for="(item, index) in data" :key="`${item.someProperty}-${index}`">
Upvotes: 9
Reputation: 7
console.clear()
Vue.component("item", {
props: ["value"],
data() {
return {
internalValue: this.value
}
},
template: `<li>Internal: {{internalValue}} Prop: {{value}}</li>`
})
new Vue({
el: "#app",
data: {
items: [{name:'a'},{name:'b'},{name:'c'}]
},
methods: {
addValue() {
this.items = [{name:'a'},{name:6},{name:'b'},{name:'c'}];
}
}
})
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<div id="app">
{{items}}
<ul>
<item v-for="i in items" :value="i.name" :key="i"></item>
</ul>
<button @click="addValue">AddValue</button>
<ul>
<item v-for="(i, index) in items" :value="i.name" :key="index"></item>
</ul>
</div>
To be more clear
Upvotes: -6
Reputation: 82459
Because arrays are mutable. The index of any given item can and will change if items are added to or removed from the array.
You want your key
to be a unique value identifying only your unique component. A primary key that you create is always better than using an index.
Here is an example.
console.clear()
Vue.component("item", {
props: ["value"],
data() {
return {
internalValue: this.value
}
},
template: `<li>Internal: {{internalValue}} Prop: {{value}}</li>`
})
new Vue({
el: "#app",
data: {
items: [1, 2, 3, 4, 5]
},
methods: {
addValue() {
this.items.splice(this.items.length / 2, 0, this.items.length + 1)
}
}
})
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<div id="app">
{{items}}
<ul>
<item v-for="i in items" :value="i" :key="i"></item>
</ul>
<button @click="addValue">AddValue</button>
<ul>
<item v-for="(i, index) in items" :value="i" :key="index"></item>
</ul>
</div>
Note that when addValue
is clicked, the list on top represents the new numbers in the array where the truly are in the array; in the middle. In the second list below the button, the values do not represent the actual location in the array and the internal and property values do not agree.
Upvotes: 51