During my work with Vue, I had some strange issues with data reactivity. In theory, everything should be fine and clear, and it’s true… but only in simple cases. If we use arrays, objects and more complex structures, reactivity can me misunderstanding for us. There are several options how we can solve this problem, but most of them are very bad. Fortunately, there is one, but very good and will work in any case: using key attribute for component and other elements on our Vue template. This post is how use it and what should we avoid when we fight with reactivity.
This problem is very common in Vue and often many developers try to avoid it using strange constructions. One of them is to re-render whole component, what it’s completely unnecessary and only waste resources. We can do this using v-if with special flag – of course v-show will not work, because it only show/hide elements, not re-render them. V-if will work, but if you will decide to use it, you must remember, that it will run full component lifecycle i.e. beforeCreated, created, beforeMount, mounted and also destroying of old component. Do we really want to do this every time? Probably not.
We can also use this.$forceUpdate() method, but it can be also problematic. Why? It re-render view of current component, in current scope. It will not update computed properties, and also, in many cases, will not refresh child components, so we must call it for each child components in lists, tables etc. It’s bad option and very problematic to implement. There is one additional thing: in normal cases, Vue will refresh necessary data automatically, so… we really don’t need to $forceUpdate() in most of cases. It’s only for very, very specific situation, not for common usage. So, what can we do?
Solution: keys, keys everywhere!
The good way is to use components and elements keys. For example, we have table with rows based on previously declared, user can click a button to add new row to table. User also has ability to remove each row. It’s typical situation, when Vue reactivity system can “don’t work” as we expected. It’s because detection changes inside arrays and objects are much more difficult and very expensive. We must show these changes Vue manually, and we can do this using key attribute. There is small example:
<table> <tbody> <template v-for="(index, row) in rows)"> <myrow :data="row" :index="index" :key="row.id" @rowDelete="rowDelete" /></myrow> </template> </tbody> </table> <button value="Add row" @click="rowAdd" />
Row.id is a unique id for each row, generator for example using UUID or something else. In rowAdd method, we just put new element into rows array (also generate UUID for it). In rowDelete, we just remove this element from array using splice method and index passed into component.
And in this case, everything will be ok, everything will work fine without any issues. Why? Because Vue will know, that elements keys will different after change (not inside one row, but in overall table). You can ask: can we use array index to do the same thing, instead of generating special ID for that? No, it isn’t good option, because when we will make changes in array, keys can change. It will force elements to re-render, and we don’t want to do this – it’s unnecessary and needs more resources.
Using key is the best option to solve reactivity issues with Vue components. After we start do this, it should be something natural and normal. It will make you work much simpler.