Array Change Detection in Vue.js
In the previous post, we saw how Vue.js watches an array for changes and automatically re-renders the page to reflect the changes. Many of JavaScript’s array functions change the array that you pass to them instead of returning a new array. This is the case for functions like push, splice, pop, shift, and more. What Vue.js does internally, is that it wraps these functions in order for them to trigger view updates as we would expect. Otherwise pushing an element to an array as in the previous post would not work, for example.
I have loaded up the example form the previous post, because even though I told you that you did not have to pay attention to the JavaScript code, there is actually something interesting to note. Notice the first line within the shuffleArray method. This line uses the splice function to make a copy of the array that was passed as an argument. This is necessary, because otherwise Vue.js will not recognize the changes that are made to the array. Now why is that? Isn’t Vue.js watching the array, and didn’t I just say that it wraps the functions to recognize when we change the array, including the splice function? Yes, that is true, but there is one exception to this. That is when changing an element’s value by using the square bracket syntax. Let’s see a simpler example of that.
In this example, we simply have an array of numbers from one to five, which is output in an unordered list using the v-for directive. There is also a button which invokes a click event handler that changes the number stored at the second position to ten. What do you think would happen if I run this code and click the button? Let’s try.
As expected we see an alert dialog with the new number, indicating that the array element has indeed been updated to ten.
But wait… The list has not been updated! Why isn’t Vue.js updating the list now that it is watching the array for changes? This is a limitation of JavaScript which causes Vue.js to not being able to detect when the value of an array element is changed like this. That is, using the square bracket syntax to update an element at a specific index. Yeah, I know… That is really not nice, but what can we do about it? As of now, we have two alternatives; one is to use JavaScript’s array functions to accomplish the same thing, because Vue.js wraps these and therefore detects the changes. Another is to use a method provided by Vue.js, named set, so let’s see how that works.
I will replace the statement that clearly didn’t work with a new statement which invokes Vue.set. This method takes three arguments; the first is a reference to the object or array on which we want to change a value – this.numbers in this case. We need to prefix with the this keyword because we are inside a method on our Vue instance. The second argument is the key that we would like to change. Because we wish to change an element within an array in this case, this will correspond to the array index. The last argument is the value that we want to set on the first argument, in this case the number ten.
Vue.set(this.numbers, 1, 10);
Note that the reference that you pass as the first argument cannot be either a Vue instance or the root data object of a Vue instance.
Running the code again, we will see that the list is now updated as we would have expected in the first place. So in cases where Vue.js cannot detect changes made to your data, you can use the Vue.set method, and Vue.js will happily trigger an update to your view.
In this post we saw a limitation of Vue.js’ change detection, but saw how to deal with it by using the Vue.set method.
Here is what you will learn:
- How to build advanced Vue.js applications (including SPA)
- How Vue.js works under the hood
- Communicating with services through HTTP
- Managing state of large applications with Vuex
- ... and much more!
One comment on »Array Change Detection in Vue.js«
Thanks for the great tutorial. I have created a jsfiddle that updates the array here https://jsfiddle.net/MarvGeek/68f6ckj6/29/ without using Vue.set which works fine, but on the real app a environment is not updating, can you perhaps tell me why??