0

There's a similar question here, but for Vue2. The solution, use Vue.set, is not valid in Vue3. I'm getting a lot of results about Vue2, but nothing about Vue3 for this yet.

There's a lot of fanciness going on in the code, but it's really quite moot. My object lives in a state in the vuex store. It has a non-enumerable property that is a function that adds records to itself like so:

class myObject extends Object {
    constructor(){
        this.add = function(id){
            isReactive(this) //=== true, because vuex
            this[id] = new myOtherObject(data);
        }
    }
}

Question:

Is there a way to force the reactive wrapping this object to know it's had these properties added?


There are other work arounds to my problem, one of which I've implemented, and is basically to include some other thing in the computed so it knows it's time to update itself. Luckily, I have a non-enumerable length property on my object that is incremented when a property is added that works perfectly:

const record = computed(() => {
    store.state.myData.myObject.length; //<- literally just an incremented ref
    return store.state.myData.myObject; //<- the problem child
});

But this is weird and hacky, and feels very anti-pattern.


Example code:

class myOtherObject extends Object {};
class myObject extends Object {
    constructor(){
        super();
        this.add = function(id){
            console.log(`Added: ${id}`);
            this[id] = new myOtherObject();
        }
    }
}

const store = Vuex.createStore({
    state: function(){
        return {
            myData: {
                test1: new myObject()
            }
        }
    }
});


const record = Vue.computed(() => {
    console.log("This isn't happening when new props are added");
    return store.state.myData.test1; //<- the problem child
});

for (var i = 0; i < 100; ++i){
  store.state.myData.test1.add(i);
}
console.log(store.state.myData.test1);
<script src="https://unpkg.com/vue@3.2.30/dist/vue.global.js"></script>
<script src="https://unpkg.com/vuex@4.0.2/dist/vuex.global.js"></script>
Randy Hall
  • 7,716
  • 16
  • 73
  • 151
  • There's no Vue.set because proxies in V3 reactivity allow to react in a precise way that doesn't require to recheck everything. This looks like XY problem. What is the actual case? – Estus Flask Feb 12 '22 at 07:10
  • @EstusFlask literally as described, computed (or watcher, tried that too) is not reacting to object in vuex store that has its own setter adding properties. No other issues with this object. Immediately works if I call another property of that same object that is being mutated instead of added. – Randy Hall Feb 12 '22 at 07:24
  • The problem is that the object is supposed to be reactive while it's not. Yes, `length` is a hack that should be avoided, already happened to me. That it may be XY problem means that there could be a solution where you won't have this problem. It's unclear why `id` doesn't exist in the store, and why `myOtherObject` is not reactive, and why you access `this` and not a state – Estus Flask Feb 12 '22 at 07:41
  • updated. `this[id]` is literally the object in question adding the property from `id` to itself. myOtherObject is irrelevant, it could be a string or anything else. – Randy Hall Feb 12 '22 at 07:51
  • Do myObject and myOtherObject classes need to be classes? Vue 3 works with class reactivity in undocumented way, with some known pitfalls. I wouldn't expect any problems from a state composed of plain reactive objects/arrays. This also depends on how `record` is used, but this primarily means that there's a problem with the reactivity of `myData.myObject`. Please, provide https://stackoverflow.com/help/mcve for your case, it's really specific to what you're doing, and why. I don't see how the question can addressed in general. – Estus Flask Feb 12 '22 at 08:10
  • @EstusFlask yes, they need to be classes. Yes, I've slightly updated my provided code to have a functional snippet. Information on said pitfalls would be helpful. Yes, there's a problem with the reactivity of myObject, that is the literal question here. I don't expect it to be reactive when it's self-updating in a way a watcher probably can't handle. This is the one caveat where the old V2 method was more useful. I'm hoping there's an analogous method somewhere in V3. – Randy Hall Feb 12 '22 at 08:51
  • I believe that Vue.set came with a price, it needed to blindly check differences in a big amount of data. There's no myOtherObject implementation. As I said, this looks like XY problem. Since there's no Vue.set (I'm unaware of any private methods in V3 that could replace it, and even then, they are internal and shouldn't be relied on), myOtherObject likely needs to be reworked to fit Vue reactivity. Even if it's external class, it can be extended and fixed for this purpose – Estus Flask Feb 12 '22 at 09:09
  • @EstusFlask yeah no I'm not reworking several thousand lines of code because I have to use one extra line in a computed property. But yes, this does appear to very specifically be an issue with class constructors. No refs or reactives set in the constructor make it outside the constructor itself, though I'm 99% certain that if the properties were set to ref or reactive after the object was already instantiated it would hold. I just don't have the energy to try that this minute. – Randy Hall Feb 12 '22 at 09:14
  • FWIW, most times the problem with classes is this one, https://stackoverflow.com/questions/67894487/vue-3-reactivity-not-triggered-from-inside-a-class-instance – Estus Flask Feb 12 '22 at 09:39

0 Answers0