Home > Software engineering >  Vue Deep Watcher doesnt trigger on data change
Vue Deep Watcher doesnt trigger on data change

Time:08-10

The countdown timer works well but the deep watcher doesnt work. I made the deep watcher log the new value of seconds to console but it doesnt although the countdown timer keeps ticking.

export default defineComponent({
  name: 'Countdown',
  data() {
    return {
      date_countdown: new Date('August 16, 2022').getTime(),
      date_current: {
        days: 0,
        hours: 0,
        minutes: 0,
        seconds: 0,
      },
      getDateDiff(date_countdown: number) {
        const date_current = new Date().getTime();
        const diff = date_countdown - date_current;
        const days = Math.floor(diff / (1000 * 60 * 60 * 24));
        const hours = Math.floor(
          (diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
        );
        const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
        const seconds = Math.floor((diff % (1000 * 60)) / 1000);
        return { days, hours, minutes, seconds };
      },
      timer() {
        return setInterval(() => {
          this.date_current = this.getDateDiff(this.date_countdown);
        }, 1000);
      },
    };
  },

  watch: {
    seconds: {
      deep: true,

      handler(newVal) {
        console.log(newVal); // to test the deep watcher; doesnt log tho
      },
    },
  },

  mounted() {
    this.timer();
  },

  beforeUnmount() {
    clearInterval(this.timer());
  },
});

I'm not sure but maybe since the timer() function changes the value of date_current instead of changing date_current.seconds individually, the deep watcher is not triggered?

CodePudding user response:

That because you watch the seconds but seconds is not a direct property in the data or a computed property, If you want to watch the changes on seconds you need to apply watch event on date_current and inside the watch function get date_current.seconds.

<script>
import { defineComponent } from "vue";
export default defineComponent({
  name: "Countdown",
  data() {
    return {
      date_countdown: new Date("August 16, 2022").getTime(),
      date_current: {
        days: 0,
        hours: 0,
        minutes: 0,
        seconds: 0,
      },
      getDateDiff(date_countdown) {
        const date_current = new Date().getTime();
        const diff = date_countdown - date_current;
        const days = Math.floor(diff / (1000 * 60 * 60 * 24));
        const hours = Math.floor(
          (diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
        );
        const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
        const seconds = Math.floor((diff % (1000 * 60)) / 1000);
        return { days, hours, minutes, seconds };
      },
      timer() {
        return setInterval(() => {
          this.date_current = this.getDateDiff(this.date_countdown);
        }, 1000);
      },
    };
  },

  watch: {
    date_current: {
      deep: true,
      handler(newVal) {
        console.log(newVal.seconds); // to test the deep watcher; doesnt log tho
      },
    },
  },

  mounted() {
    this.timer();
  },

  beforeUnmount() {
    clearInterval(this.timer());
  },
});
</script>

This is a working code example.

CodePudding user response:

If i'm not mistaken when you make this kind of equation;

this.date_current = this.getDateDiff(this.date_countdown);

watch just loses object reference since you set an entirely new object to it and vue uses proxies for reactive properties. So it loses its prior proxy and sets a new one but it doesn't trigger change. (maybe setting immediate: true helps but it's not well suited for this case.)

if you watch date_current.seconds then your watcher watches a primitive value and detects the change. (which is a better way to do it.)

Hope it helps.

  • Related