Search code examples
javascriptvue.jsvuexvuetify.js

vue.js watch not updated


I'm new to vue. I'm now trying to update a couple of variables based on the change of another computed variable.

This computed variable is taking the values from a Vuex store and works as should. I see the values change. In order to calculate the derived variables I've created a watch that watches the computed variable and then updates these derived values. This watch is called two times during start-up and then no longer, although the computed values keeps updating. What am I doing wrong.

This is working:

...
computed: {
    lastAndMarkPrice() {
      return store.getters.getLastAndMarkPriceByExchange(
        "deribit",
        store.getters.getAsset
      );
    },
...

this part is not working:

...
data: () => ({
    lastPriceUp: false,
    lastPriceDn: false,
    markPriceUp: false,
    markPriceDn: false,
  }),
...
watch: {
    lastAndMarkPrice (newValue, oldValue) {
      console.log(newValue, oldValue);
      this.lastPriceUp = newValue.lastPrice > oldValue.lastPrice;
      this.lastPriceDn = newValue.lastPrice < oldValue.lastPrice;
      this.markPriceUp = newValue.markPrice > oldValue.markPrice;
      this.markPriceDn = newValue.markPrice < oldValue.markPrice;
    },
  },
...

Solution

  • By default a watch is shallow. If a new object is assigned to lastAndMarkPrice then the handler will be called but it won't check for mutations of properties within that object.

    To create a deep watcher you'd do something like this:

    watch: {
      lastAndMarkPrice: {
        deep: true,
    
        handler (newValue, oldValue) {
          // ...
        }
      }
    }
    

    https://v2.vuejs.org/v2/api/#watch

    Usually that would be the correct solution but your use-case is slightly more complicated because you need access to the old values. Using a deep watcher won't help with that as you'll just be passed the same object.

    To get around that problem you'll need to take copies of the old values somewhere so that you still have them available to compare with the new values. One way to do that would be to have the computed property take a copy:

    computed: {
      lastAndMarkPrice() {
        const prices = store.getters.getLastAndMarkPriceByExchange(
          "deribit",
          store.getters.getAsset
        );
    
        // I'm assuming that prices is initially null or undefined.
        // You may not need this bit if it isn't.
        if (!prices) {
          return null;
        }
    
        return {
          lastPrice: prices.lastPrice,
          markPrice: prices.markPrice
        }
      }
    }
    

    With the code above, each time the values of lastPrice or markPrice change it will re-run the computed property and create a new object. That will trigger the watch handler and, importantly, you'll get two different objects passed as the old and new values. You don't need to use deep in this case as the object itself is changing, not just the properties within it.

    You could also shorten it a little with...

    return { ...prices }
    

    ...rather than explicitly copying the two properties across.