Search code examples
javascriptvue.js

Why doesn't this data live update on change in my vue component?


I'm updating a relatively complicated component (because it is used in many places on a website, and the update, although being pretty simple in principle, can't change the html structure of the component because it will break things elsewhere). I need to add a certain footnote in some cases when window size is below 768px in width.

Basically, my solution is to have a span that shows v-if="mobile_footnote" is returning true. So I was thinking, on mounted I'd check if the footnote is passed as true from the parent, and if so, I'd have a listener that checks the size of the innerWindow and when it's below 768 I'd set the mobile_footnote to true, and when it's larger I'd set it to false. However it's not behaving how I'd expect.

So I have a prop that is passed from the parent called footnote (default: null), and a data value called mobile_footnote (default: false)

I have this in my mounted()

if (this.footnote) {

  // Set the mobile_footnote to true on window load, this works fine
  let width = window.innerWidth;

  if (width < 768) {
    this.mobile_footnote = true;
  }

  // If user resizes, update mobile_footnote to true/false depending what's needed, this is broken
  let onresize = function() {
    let width = window.innerWidth;
    this.mobile_footnote = true;

    if (width < 768) {
      console.log('footer true')
      this.mobile_footnote = true;
    } else {
      console.log('footer false')
      this.mobile_footnote = false;
    }

    console.log(this.mobile_footnote)
  }

  window.addEventListener("resize", onresize);
}

The issue I'm coming across is that, although in console logs the data mobile_footnote does update at the correct window widths, it doesn't live update the v-if span except on refresh. Is there a reason the live update isn't working?

Based on Vue's lifecycle hooks it seems like it should update once data updates, right? Or am I misunderstanding the lifecycle hooks?

Lifecycle hooks from Vue says that when mounted, a change to data will re-render and patch the component

What am I missing?


Solution

  • This issue is related to the scope of onresize not being the same scope as the component. Arrow function would fix the problem, e.g. const onresize = () => { ... }, but really with Options API all functions should be within the "methods" option of the component:

    methods: {
      onresize() {
        let width = window.innerWidth
        this.mobile_footnote = true
    
        if (width < 768) {
          console.log('footer true')
          this.mobile_footnote = true
        } else {
          this.mobile_footnote = false
          console.log('footer false')
        }
    
        console.log(this.mobile_footnote)
      }
    },
    mounted() {
      if (this.footnote) {
        // Set the mobile_footnote to true on window load, this works fine
        let width = window.innerWidth
    
        if (width < 768) {
          this.mobile_footnote = true
        }
    
        window.addEventListener('resize', this.onresize)
      }
    },
    // remember to remove event listeners before unmounting
    beforeUnmount() {
      window.removeEventListener('resize', this.onresize)
    }
    

    Alternatively, I can't help but find the current solution over-engineered. Without using any of the above code, the footnote can be dynamically displayed based on screen size using CSS media query. Where the footnote element has class "footnote":

    @media (min-width: 768px) {
      .footnote {
        display: none;
      }
    }