Reactivity: Document that class instances using native private fields are incompatible with ES6 proxies and therefore Vue's reactivity
Issue triggered by this issue in core: https://github.com/vuejs/core/issues/7240
A class such as this one will throw an error when the getter is accessed on a reactive proxy for it:
class X {
#a = 'secret'
get getA() { return this.#a }
}
const x = reactive(new X())
x.getA // throws
Seee the example in the linked issue for a demo.
But this works
class X {
#a = 'secret'
get getA() { return this.#a }
}
const p = new Proxy(new X(), {
get(target, propname, receiver) {
return Reflect.get(target, propname, target);
}
});
console.log(p.getA);
that's ...interesting. I need to look up what the specific (but necessary) circumstances were that made this break in our implementation, can't recall.
As far as I know, Proxy can't hook method call proxy.method1();. Proxy can only trap the property getter for the method proxy.method1.
But getter and setter are not methods. They are called internally not externally. Reflect.get() and Reflect.set() work perfectly.
Ah, you switched out the receiver for the target. That's not the usual use for Reflect, but maybe we can use this to work around this problem in a way.
Edit: on second thought, I don't think this is a path to a solution as we need receiverto be the proxy so we can track other possible property reads in getters.
You can save the reactivity for calculated properties like this
import { Ref, ref } from "vue";
class OldClass {
val: number;
constructor(){
setInterval(() => this.val = Math.random(), 1000);
}
get my_value(){
return this.val;
}
}
const store: Record<string, Ref<any>> = {};
class MyClass extends OldClass {
vue: Record<keyof OldClass, Ref<any>>;
constructor() {
super();
//@ts-ignore
this.vue = new Proxy(this, {
get(target, propname, receiver) {
const d = Reflect.get(target, propname, target);
if (typeof propname != "string") return d;
if (!store[propname]) store[propname] = ref(d);
if (store[propname].value != d) store[propname].value = d;
return store[propname];
},
});
}
}
in vue
const my = new MyClass()
// ...
<div>{{ my.vue.my_value.value }}</div>
<!-- value is reactive! -->