Skip to content

Vue3 does not react to class field internal updates in the same way as Vue2

I have noticed that while in Vue2 you can bind an element to the property of a class, and the element will update when this class property is changed from somewhere outside of the Vue world, this seems not possible in Vue3.

I have created two simple examples here to show what I mean:

Vue2: https://codesandbox.io/s/vue2-6hztv

Vue3: https://codesandbox.io/s/vue3-o2rfn

There is a class that has an internal timer which will increment the class field. In Vue2 the element bound to myClass.field is properly updated but in Vue3 nothing happens.

My questions are

1. Why is there a difference between Vue2 and Vue3 here?

2. How can I achieve something like the working Vue2 example but in Vue3 ?

Please note that I cannot run the timer in a Vue lifecycle method. The class field needs to be updated by itself.

Here is the Vue3 code which does not work:

HTML:

<div id="app">{{ myClass.field }}</div>

Javascript:

class MyClass {
  field = 0;

  constructor() {
    setInterval(() => {
      this.field++;
    }, 1000);
  }
}

export default {
  data() {
    return {
      myClass: new MyClass(),
    };
  },
};

Answer

As explained in this answer, proxy object is created in Vue 3 to enable the reactivity. this in constructor refers to original class instance and not a proxy, so it cannot cannot be reactive.

The solution is to separate class constructor and the setup of side effects that expect this to be reactive. A setup method can implement fluent interface pattern to make it simpler to use:

class MyClass {
  field = 0;

  init() {
    setInterval(() => {
      this.field++;
    }, 1000);

    return this;
  }
}

In options API it is:

  data() {
    return {
      myClass: new MyClass(),
    };
  },
  created() {
    this.myClass.init();
  }

In composition API it is::

  const myClass = reactive(new MyClass()).init();