Preserving `this` identity (Vue 2)
thecodewarrior opened this issue · 0 comments
Background
One of the trickiest pitfalls with Vue reactivity is the fact that adding reactivity involves wrapping an object. The original object isn't reactive, only the wrapper created with new Vue({data: {...}})
.
let original = {wow: 10};
let reactive = new Vue({data: original});
original.wow = 20; // this doesn't cause reactions
When using @VueStore
this becomes a problem if you capture this
in the constructor:
@VueStore
class AsyncStore {
value: string = null
constructor(id) {
requestData(id).then((v) => {
this.value = v; // the captured `this` refers to the original, not the reactive wrapper
});
}
}
Proposal
It would be great if we could inject reactivity into the original object, that way the captured this
would be reactive. #27 is my implementation of this.
Implementation
The principle behind my solution is double-initializing the object. After the store is constructed we rip out the data, then we turn around and ask Vue to initialize this now-empty object with the extracted data.
In order to do this we have to extend Vue
while not extending the Vue
constructor. If we extended the constructor, Vue would initialize the object before we get a chance to add our data.
Details
The way I implement this quasi-inheritance is that, instead of having Vue in the prototype chain, I literally copy the contents of the Vue
prototype into the store's prototype.
The double-initialization is feasible because the Vue constructor just calls this._init(options)
. Because we copied the prototype, all we have to do is take the initialized store, rip out the data, then call store._init({data: extractedData})
.
Vue 3
See #29
It might be worth emulating the Vue 3 API limitations with regard to inheritance. i.e. we could require you subclass VueStore
to get the new this
safety, to ease the transition to Vue 3.