Categories
Vue

Making Non-Reactive Changes Reactive with Vue.set

Spread the love

If we’re making changes to reactive properties in Vue apps that aren’t picked by Vue automatically, then we need to use the Vue.set method.

In this article, we’ll take a look at reactive and non-reactive changes and how we can use Vue.set with non-reactive changes.

Reactivity

Changes for reactive variables are tracked by defining traversing objects and defining getters and setters for them.

They’re used for tracking dependencies and notify Vue of changes.

Every component has a corresponding watcher instance to watch for property changes.

Each time a dependency setter is triggered, the watcher is notified, and cause the component to re-render.

Objects

Vue can’t detect property addition or deletion.

This is because the getter and setter conversion process requires the property to be present so that Vue can make it reactive.

For example, if we have:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>app</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          a: 1
        }
      });
      app.b = 2;
    </script>
  </body>
</html>

Then the b property won’t be watched automatically.

We can’t add properties to the root level of an already created instance.

But we can add new properties to a nested property with Vue.set .

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>app</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      {{someObject.b}}
    </div>
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          someObject: {}
        }
      });
      Vue.set(app.someObject, "b", 2);
    </script>
  </body>
</html>

We call Vue.set with the property with the object to add the property to.

Then the property name, and then the value.

We can also use the this.$set method in the Vue instance object itself.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>app</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      {{someObject.b}}
    </div>
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          someObject: {}
        },
        mounted() {
          this.$set(this.someObject, "b", 2);
        }
      });
    </script>
  </body>
</html>

to do the same thing.

We can also use Object.assign and assign it to the same property to add the property:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>app</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      {{someObject.b}}
    </div>
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          someObject: {}
        },
        mounted() {
          this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 });
        }
      });
    </script>
  </body>
</html>

Reassignment will also trigger Vue to re-render the component.

Arrays

Vue can’t detect some changes to arrays.

It can’t detect directly setting an item with the index.

And it can’t detect modifying the length of the array.

So these 2 changes aren’t reactive:

vm.items[1] = 'x'
vm.items.length = 2

To make the changes, we can use Vue.set again:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>app</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      {{items[1]}}
    </div>
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          items: ["foo", "bar"]
        }
      });
      Vue.set(app.items, 1, "qux");
    </script>
  </body>
</html>

We call Vue.set with the array property, index, and the value to set for the index.

We can also use splice :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>app</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      {{items[1]}}
    </div>
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          items: ["foo", "bar"]
        }
      });
      app.items.splice(1, 1, "qux");
    </script>
  </body>
</html>

The first argument is the index to change, the 2nd is how many items to change, and the 3rd is the value to change the entry with the given index to.

Also, we can call this.$set in the component instance:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>app</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      {{items[1]}}
    </div>
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          items: ["foo", "bar"]
        },
        mounted() {
          this.$set(this.items, 1, "qux");
        }
      });
    </script>
  </body>
</html>

this.$set takes the same arguments as Vue.set .

Conclusion

The Vue.set method lets us make non-reactive object and array operations reactive.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *