Computed not updating when data changes


#1

And there is no way to debug it. No visibility into the setter. No documentation on how vue internals work or where to see subscriptions and subscribers.

Incredibly frustrating. Every day I work with vue, it is a constant struggle because sometimes a prop/watcher/computed props/component props etc wont update/fire when the underlying data changes. Almost always this happens due to some reactivity failure (not related to Vue.set()) but rather chained vue instances, vuex, observing dom nodes, etc where you don’t know if reactivity is going to succeed.

I need documentation and a way to inspect the subscriptions of observed data, and I need a way to peer into a subscriber and see/throw a breakpoint into it’s dependency.

As an experienced vue user 100% of my frustration with the library comes for several day debugging sessions where I try to isolate the reason that reactivity is failing.

I really can’t say this enough times. I will say it over and over and over again. I need documentation on/the ability to view and debug subscriptions and dependencies in vuejs.


#2

For the first jsfiddle, your button needs to be in the id tags.
image

https://jsfiddle.net/isogunro/5oepaa9o/20/


#3

You are correct, I made a quick mistake creating these examples. I have a nearly identical situation in a large scale app I am working on and none of the examples cause the computed property to update, when the function is triggered from JS (not a button).

My point isn’t to get a working example. Creating working examples is easy. The problem is when there is a real issue in the reactivity system I have no way of debugging it (or even seeing what is subscribed to an attribute).


#4

I actually just copied and pasted my entire app into a fiddle and it works. Which is actually my point. I have absolutely no hope of debugging my issues in vue to see what exactly is breaking reactivity.


#6

Have you tried using chrome extension for developing vue applications? It should help with debugging. https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=en


#7

Yes, but it offers, again, no visibility into the internal reactivity…


#8

Hi there.

Sorry to hear you are having a hard time with Vue.

I won’t be able to give you what you really want (a way to easily inspect the reactivity system or at least more docs) right now, but I would like to offer to discuss strategies to debug reactivity problems.

While I don’t mean to doubt your experience and can understand that sometimes, problems in reactivity can be a little hard to track down, I’m honestly a little surprised by the intensity this problem seems to have for you, so I’m kind of hoping we can improve your situation a bit by talking about specifc issues.

Would you be willing to provide an example of a situation where you would get stuck in a reactivity problem in Vue, and where some sort of insight under the hood would have helped? I won’t be able to say anything really helpful with just the general sentiments you expressed so far, unfortunately.

Also I would invite you to open an issue on github, probably in the repository of the vue-devtools, and write down your feature request, with a little more detail how it should work to be useful to you as the user. While I can’t promise this will be possible yo implement with a realistic amount of effort, our maintainers already added some pretty amazing things with the devtools recently, so who knows what they might come up with!

Another i´ssue in the vuejs.org repository concerning docs would also make sense.


#9

@LinusBorg

There are 3 kinds of issues I run into with vue almost daily:

1. Underlying data changes, but computed’s do not update in dev tools/re-evaluate unless they are subscribed to in the dom. This has been a major issue when using computed’s to track and pass on scroll state where I may never want to render the value in dom: https://jsfiddle.net/5oepaa9o/53/

Notice how window.alert() never gets called?

But look, it works just fine here: https://jsfiddle.net/5oepaa9o/56/

It actually drives me nuts in the dev-tools, or when firing functions from within computed’s. During development it appears that the binding is broken, breakpoints never fire. Computed properties should update regardless of whether or not the dom is subscribing. I don’t want to write a watcher, they have their own issues which I won’t go into here.

I often manually refresh state tree in dev tools to re-evaluate which is a major hassle.

FIX: Have dev-tools update computed’s regardless of whether or not dom is subscribed. Honestly, who writes a computed that they do not wan’t evaluated?

2. Underlying data changes, and computed/watchers/etc never re-evaluate, not on dev-tool state refresh, not on manual check in the console. Not sure why, could be a bug in my code, but it would be very reassuring to be able to place a breakpoint and see that subscription fire, which would immediately step into code and show where it fails.

FIX: Allow visibility on a vue instance into the getters/setters or pub/sub model so that we can (a) make sure our subscriptions are correct (b) debug them if they arent.

3. Underlying data changes, and computed/watchers/etc re-evaluate, but dom does not update (have to call $forceUpdate().

FIX: Not sure yet, as I haven’t quite figured out how to repro this one yet.


#10

Look here: https://jsfiddle.net/5oepaa9o/71/

This is a very basic sub/pub model. It is easy to see for each prop exactly who is subscribing. Even further, I can drop a breakpoint in each subscription and follow it to see exactly when and what is updating. Would really love to be able to do this for vue, if not in the console then in the dev tools, especially when not in production.

It just really sucks that because of the issues mentioned above I have to manually implement this type of hacky code to have predictable functionality. This is what vue is supposed to handle!


#11

About 1.

This is a simple misunderstanding about how computed props work: They are evaluated lazily, which means: only if they are used.

You never use your computed prop anywhere. Computed props should not have any side effects, they should only return a computed value. if that value is never used anywhere, there’s no need to computed it.

So I would ask: Who wants their computed properties updated when they don’t use the updated value anyway?

Since your first example never uses the computed prop, I assume you want to instead use it like a watcher and cause a side effect? Then actually using a watcher is the way to go, even if you don’t like them.

A change of this behaviour is out of the question, I hope my explanation makes it clear why.

But I was checking out docs about this and it seems were are indeed not explicit about this. There should probably be added a clarification about this behaviour somewhere around here.

About 2.

Since you didn’t provide an example of this, I can’t judge wether these instances are misunderstandings of expected behaviour like 1., real bugs or just hard to find mistakes in your code, so my answer will be shorter:

You can do that today in a way if you understand the internals. you can inspect a component and see watchers and deps for the render function as well as all computed props etc, but it’s correct that this is neither nicely documented nor very easy. We are open to suggestions and input about how this could work in a useful way.

Usually though, these kinds of problems, where the reactivity seems to work but DOM doesn’t see to update are usually related to missing key props in the render function and similar things, so debugging the reactivity getters and setters might not help you very much as the problem is something else, i.e. Reactivity works fine, but the DOM isn’t patched correctly because of missing keys in the markup.

About 3.

I can’t say anything except pointing to the obvious Reactivity caveats which I’m sure you are aware about. I personally don’t find them hard to track down once you know them, but it might be different for you.


#12

Well, of course you can set breakpoints, you implemented it in your own code and it works by manually adding subscriptopns. you could also set breakpoints in Vue, you “just” have to understand how it works. And I don’t think you expect a “real-life” reactivity system that takes care of edge cases can be that simple, right?

Well, tbh while I agree that some things could be improved (what can’t?) I’m still suprised that you have such a hard time, to be honest. Reactivity has it’s caveats, but once you know about them debugging is simply following the this.something calls and see where you fell for one of them, in 99% of cases - at least that’s my experience, and as far as I can tell, that of most of the other people I meet here. I rarely see the same person ask a Reactivity problem more than once.

I’m not sure I can offer you much more than I explained so far, combined with the offer to maybe open an issue and suggest improvement for the devtools and see what’s possible.


#13

Let’s talk about 1 now as it is more concrete.

I understand that computed props are evaluated when dependencies change, “A computed property will only re-evaluate when some of its dependencies have changed.” This does not mean lazy.

I do however remember discussions a long time ago around “lazily”, although for some reason it isn’t in current docs.

Who wants their computed properties updated when they don’t use the updated value anyway?

My response is simple, just because I am not using them in dom does not mean I am not using them.

I understand the design decision not to have side effects. However, there are 2 use cases where I don’t want them to evaluate lazily:

1. I want side effects, but do not like the syntax of a watcher (and prefer computed):

    focused: function () {
        if (this.x > 50) {
            this.focusDomEl();
            return true;
        }
        else {
            this.unfocusDomEl();
            return false;
        }
    }
}

Possible fix here could be:

(a) not lazily evaluating things that need to be evaluated
(b) alwaysComputed, which would have side effects and the power of watchers, with the syntax of computed’s (this is lame because it pollutes the api)
© isn’t there already some kind of flag inside a computed function lazy: false like cached: false to have it not be lazy? (again, lame because it makes computed’s complicated)

I really have not heard a real reason for lazily evaluating computed’s, why are dev-tool’s not considered an evaluation at least?

2. I just wrote a computed function, am validating it’s output, and haven’t yet consumed it in dom:

This is often the case for me. I am writing a computed function and wan’t to validate it in dev tools. Well, guess what, it doesn’t work because I haven’t consumed it in dom.

you can inspect a component and see watchers and deps for the render function as well as all computed props etc

I would love to know exactly where the watchers are for data and the deps are for computed props.


#14

You are entitled to dislike their syntax, but personal preferences at that level won’t make use change behaviour of the framework, I’m sorry to say.

I can understand that situation to an extend, yet I don’t really see how it’s a big problem to simply do add it to the dom to get it to work. I understand it’s a minor inconvenience, and any inconvenience, no matter how small, can get on our nerves at times. But that won’t justify to make computed props not lazy.

Computed props that are not lazy will become a nightmare whenever they contain more complex calculation. It’s a performance optimization that we won’t give up to suit your workflow, to be brutally honest.

OTOH, you might think we could add an option to make them non-lazy to switch that flag quickly in these validation cases - but then you will inevitably forget to switch it bak and produce another bug. It’s not a worthwhile option - in my opinion.

You can open a feature request for this nontheless and see if you find others that agree, I’m not the one deciding features :wink:

Inspect a component instance. You will find a ._watcher property, as well as a .computedWatchers property.

The watcher instances then have their dependency objects in a .deps property.

And all reactive objects have their Observer instance stored in the ob property.

Mabe start from there and see where it takes you. Suggested reading would also be the Observer/Watcher/Dep implementations here


#15

I can appreciate staying strong on performance here, but:

1. This needs to be widely recognized as a caveat to computed properties and change detection here:

it will update any bindings that depend on vm.reversedMessage when vm.message changes

2. There needs to be console warnings in development mode

> Computed property 'x' is listed, but never consumed in it's template; as such it will not be evaluated on data change (it is not reactive).

3. Dev-tools should STILL register these computed props as a dep and evaluate these properties (while giving the warning above).

Any of the above 3 would have saved days of my life debugging this. Not your fault, but hey, we should fix this and I am happy to help.

Now lets talk about computed, watchers, and perf:

Computed:

1. Complexity: It is really tragic that complexity here comes at a trade-off for ‘performance’ (sigh yet another change detection caveat).

2. There are a lot more valid scenarios for writing code that does have side effects, vs unused computed props.

Is anyone actually doing this? Is there a real use case for not using your computed props? Is vue really going to put ‘training wheels’ on for ‘performance’ and, a) take away the developers ability to write advanced code, b) hurt the development experience and c) add yet another change detection caveat? Developers should have sharp knives. It is outside vue’s responsibility to decide when my declarative code should be run…especially to try and save developers who forgot to delete unused computed’s from ruining their own performance. Which is why:

3. It is a better solution to simply warn the user that there are unused computed properties that will affect performance.

Watchers:

  1. It is not just my opinion that watchers are inferior expression of change detection, it is vue’s as well:

The above code is imperative and repetitive. Compare it with a computed property version:

it is often a better idea to use a computed property

  1. Watchers don’t scale: in the example, 2 watchers are required to do the work of one computed, which only gets worse if you need to track 10 variables in your watcher…you have to write 10 watchers…

At the end of the day, computed vs watcher are two different formats on top of a simple concept. Track dependencies => trigger actions + return values.

There are cases where one syntax is preferred, but vue should be un-opinionated about what can be done inside of each. A watcher should be able to return a value, and a computed should be able to trigger side-effects.


#16

But that won’t justify to make computed props not lazy. Computed props that are not lazy will become a nightmare whenever they contain more complex calculation. It’s a performance optimization that we won’t give up to suit your workflow, to be brutally honest.

I understand that caching is an optimization, I don’t understand how ignoring intentional business logic is a “performance optimization”.

OTOH, you might think we could add an option to make them non-lazy to switch that flag quickly in these validation cases - but then you will inevitably forget to switch it bak and produce another bug. It’s not a worthwhile option - in my opinion.

That I can definitely agree with.

I worked up 3 examples of essentially the same code, written with the tools vue provides. You can see which one is the cleanest and least repetitive. The only difference is that the last one, the cleanest one, will fail if I don’t consume the computed in dom.

1. Watch w/ Api call:
https://jsfiddle.net/5oepaa9o/120/

2. Computed w/o Api call, api call onchange:
https://jsfiddle.net/5oepaa9o/128/

3. Computed w/ Api call:
https://jsfiddle.net/5oepaa9o/116/

I know which one I prefer…

I also am using vuejs in a lot of code where there is no dom involved. I am building api’s using vue (server side as well) instead of using simple classes because I love vue’s class-like structure (but with way more power via computed + reactivity). In these instances, all data ends up in a function call anyways. Something to think about.


#17

I think I see it now. It’s about conditional rendering isn’t it…you don’t want to compute values for components/props that are hidden.

This way you can render only what matters. Any calculations that do data manipulation/api calls/side effects wouldn’t be appropriate here for this reason.

And as you can see above, watches are just way too verbose, the syntax isn’t very good (lets not pretend it is), but they represent logically what I am looking for.

There are two use cases:

  • Performant, lazily evaluated (ideal for conditional dom use), requires return + no side effects
  • Precise, always correct (ideal for api’s/side effects, data flow), return or side effects or both

And there are some syntax’s:

  • Imperative: Single dependency (watch style) or Multi dependency (not currently supported)
  • Implied: Single & Multi dependency (computed style)

Looks to me like all this got conflated into two common options, that don’t satisfy all cases, because we didn’t know how to name implied multi-dependency observations with no return value.

A solution could be allowing a new structure for watch that allows multiple dependencies and possibly a return value (ie. computed format).

watch: {
    total: {
        return this.x + this.y;
    }
}

#18

I hope it’s ok that I skip parts of your first reply since in the second, you saw the “light”:

The ponly exception will be a quick reaction to the examples you provided: The second one is actually the usual way to handle this and while I can respect that you don’t like it, I have to say I do. It nicely cleans up responsibilities between data generation and side effects.

My blunt gut reaction to this is: If you use a tool in a way it was not intended to be used, you should expect to find some rough edges instead of blaming the tool for not beding to your usecase.

So on to the second reply:

I can actually agree with this for the most part. Watchers don’t have the nicest syntax around. The fact that we usually don’t consider this a problem is that in the scenarios that Vue is intended for (controlling UI), we find that you rarely need watchers at all, and when you do need one, it’s usually not for a lot of different data props, but only one.

So to be honest, we didn’t mind a bit of not-as-nice syntax for something that shouldn’t be encouraged to be used, and doesn’t have to be used in most situations.

That doesn’T mean we won’t improve it if someone like you comes around with a good suggestion and willingness to help - we just have bigger painpoints on the list that usually keep this topic from reaching the top of the todo list for ourselves.

That would be nice, but the problem with this is the following: In order to register the dependencies x and y, some code has to run that actually triggers the observer’s getters on the x and y properties.

That’s basically how computed props work: the first time they are run (lazily) after being (directly or indirectly) invoked during render, we register those getter calls and can consequently track all dependencies of this computed prop and trigger a re-evaluatuation if one changes.

We can’t do the same for watchers, because the specific requirement is that watchers do not require being used in the template. But then how to register the dependencies?

  • Well, we could simply run the watcher’s function once, but then we would trigger side effects at a moment the developer didn’t intend for them to occur, so that’s not a solution.
  • We could also manually tell Vue which properties to watch. That’s how it currently works - we tell Vue "watch this prop x" by defining watch: { x:.... }

There’s no real alternative that would allow us to inspect the watcher function’s body,intelligently and automatically extracting dependencies without actually running the code, at least it’s not appearant to me.

So while I understand the requirement, this vision for a better watcher feature, I don’t really see a road to that right now. Maybe in the future, when we switch the implementtion to proxies this might be possible, but I’m not convinced.

As a small sidenote, if you ever need a watcher for a lot of properties and want to save yourself from repetition, there’s this - it’s kinda ugly, but rather short and “works”:

https://jsfiddle.net/Linusborg/rt09wvwv/5/

It juses the underlying (public) $watch API, which also accepts a function as the dependency definition instead of just a single property name.

And with or without changes in Vue Core, I think one could write a small plugin that wraps this functionality into something more declarative.


#19

We could also manually tell Vue which properties to watch.

This fiddle doesn’t run, but there is no reason this (or something similar) could not be expanded into current watcher format, and become a new watch format. I also feel like this format is a lot more intuitive and consistent with computed properties, while being more powerful.

https://jsfiddle.net/rt09wvwv/14/

Ultimately, computed and watch have different functionalities, but its only the limitation of js that is preventing the two from having nearly identical syntax.

Thanks for that fiddle, you are right that is better than a complex watcher but still not ideal.

To summarize:

1. Better watcher syntax https://github.com/vuejs/vue/issues/7673
2. Update docs to include lazy evaluation https://github.com/vuejs/vue/issues/7672
3. Possible console warn for unused computed prop https://github.com/vuejs/vue/issues/7674
4. Dev-tools should evaluate computed props https://github.com/vuejs/vue-devtools/issues/622


#20

@FEA5T having spent the last couple of years learning how to use computed properties properly, but without really knowing how they worked, I posted a question a few weeks ago, where you might find @LinusBorg’s answer + links illuminating:


#21

Thanks @davestewart for sharing that last post, indeed that was extremely enlightening. I came across this post because I faced a similar issue whereby one of my computed functions was not being updated when a form field linked to my data model was being changed.

Reading from the article on reactive engines,

Libraries like Vue.js, MobX or Ractive.js all use a variation of getters/setters to create observable data models.

is when I realised that my data model was not being implemented properly. I had setup my data with a custom object which gets populated from my server based on the page requests, ie different pages have different extra (custom) data needed to be displayed. This works well for static display of the data, but is not reactive as my browser debug console reveals,

> componentData
  Object { status: Getter & Setter, menus: Getter & Setter, permalink: Getter & Setter, logo: Getter & Setter, posts: Getter & Setter, homepage: Getter & Setter, rest: Getter & Setter, type: Getter & Setter, single: Getter & Setter, archive: Getter & Setter, … }
> componentData.custom
  Object { accommodation_type: (4) […], amenities_type: (3) […], … }

my componentData is an object I construct at initialisation and which I use to populate my vue component data. It is initially a simple object, but I can see in my console that the vue engine has made it reactive by adding getter/setter methods to all its members. However, it is quite clear that the data members residing in the ‘custom’ child object are not reactive as none of their members have getter/setter methods. Hence i had to ensure that my form fields data where defined in the initial construct to make them reactive,

> componentData.form
  Object { acm: Getter & Setter, … }

so to come back to @FEA5T original question, this is one of the tell tale signs that the data members needed to update my computed function have been identified and made reactive by the vue engine. Not really a debugging tool per se, but nonetheless a useful factor to look out for when debugging applications.