What key to use for v-for when elements have no id?
Asked Answered
G

1

11

When using v-for, it is strongly recommended to include a key. Furthermore, as explained here, using the array index as your key doesn't really help.

If your elements have an id property, then that's great - you could just use that as your key. But what about when your elements don't have an id property? What should you use as your key in that scenario?

Ginny answered 4/6, 2019 at 18:42 Comment(1)
<el v-for="(item, key) in items" :key="key" />. Do note this will fail to produce expected results when you actually care about elements order. For example, if you reorder them, it won't trigger an update (unless you use unique keys that are bound to an actual item property).Crosspurpose
I
8

Better solution

A better solution would be to use an external library to generate a hashcode from the value of your object and use that as an id. i.e. object-hash

An example using object-hash

const hash = objectHash; // this should be the import i.e. require('object-hash');

new Vue({
  el: '#app',
  template: `
  <div>
    <p v-for="item in itemsWithHash" :key="item.key">
      {{item.name}} {{item.lastname}}<br>key: {{ item.key }}
    </p>
  </div>
  `,
  data: () => ({
    items: [
      { name: 'john', lastname: 'doe' },
      { name: 'bob', lastname: 'smith' },
      { name: 'alice', lastname: 'james' }
    ]
  }),
  computed: {
    itemsWithHash() {
      return this.items.map(i => ({ ...i, key: hash(i) }));
    }
  }
});
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/object_hash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

OK Solution

You can use the index of the iteration as a key, but keep in mind that this will not trigger changes on the view if you update the item by index. (Vue uses the key to detect changes, so it wont re-render the template if the key is not updated)

<div v-for="(item, i) in items" :key="i">
    // some content.
</div>

Note the example below where mutating the items directly by index does not update the view but prints the change to the console:

new Vue({
  el: '#app',
  template: `
  <div>
    <p v-for="(item, index) in items" :key="index">
      {{item.name}} {{item.lastname}}<br>index: {{ index }}
      <button @click="changeForMary(index)"> Replace with Mary Smith</button>
    </p>
  </div>
  `,
  data: () => ({
    items: [
      { name: 'john', lastname: 'doe' },
      { name: 'bob', lastname: 'smith' },
      { name: 'alice', lastname: 'james' }
    ]
  }),
  methods: {
    changeForMary(index){
      this.items[index] = { name: 'mary', lastname: 'smith' };
      console.log(`index ${index} changed to ${JSON.stringify(this.items[index], null, '\t')}`);
    }
  }
});
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/object_hash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
Iliac answered 4/6, 2019 at 18:46 Comment(3)
I think you can use :item="item" to get a specific binding for each element, since your iteration key is itemPrimacy
@LenJoseph No you shouldn't, the Vue docs says: "Don’t use non-primitive values like objects and arrays as v-for keys. Use string or numeric values instead." vuejs.org/v2/guide/list.html#Maintaining-StateGilbreath
I was about to use object-hash, but then I saw it uses cryptographic hashes.. A better library for this would be one that uses simpler hashing, like xxHash or CRC - cryptographics are way overkill for most usecases.Subaudition

© 2022 - 2024 — McMap. All rights reserved.