davestewart / vue-class-store

Universal Vue stores you write once and use anywhere
https://github.com/davestewart/vue-class-store-demos
MIT License
277 stars 13 forks source link

Preserving `this` identity (Vue 3) #29

Open thecodewarrior opened 2 years ago

thecodewarrior commented 2 years ago

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 reactive({...}).

let original = {wow: 10};
let reactive = reactive(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 make the original object reactive, that way the captured this would be reactive. This is possible, but it's a tradeoff between inheritance and robustness.

Implementation

The tradeoff is between being able to extend a non-store class and being able to use this.

Because Vue 3 uses proxies for reactivity, we can't hack the reactivity into an ordinary instance, like I do in #27. However, because Vue 3 uses proxies, we also don't have to know the data up front. New properties on a reactive object become reactive.

In principle, all we need to do for reactivity is make a constructor that returns reactive(this). That limits our ability to use inheritance, unfortunately, so we'll want to provide both options.

Your two options would be (as a user of the library):

In both cases @VueStore would be responsible for creating watches from the 'on:foo'() methods, but it would only wrap your object in a reactive proxy it doesn't already extend VueStore.

Vue 2

See #28

thecodewarrior commented 1 year ago

Looking this up again, inheritance may actually be possible: https://stackoverflow.com/questions/64715536/how-to-capture-proxy-set-actions-from-a-class-constructor/64715857

thecodewarrior commented 1 year ago

After some more careful reading, inheritance isn't plausible. That stackoverflow post is about changing the final prototype of the object, but you can't provide a custom this instance itself. It could probably be hacked in by creating a duplicate, identical class hierarchy with the root class edited to have VueStore as the superclass, but the complexity of that is far beyond what's reasonable.