Vue.js refs are undefined, even though this.$refs shows theyre there

I solved this by using v-show instead of v-if.

I had the component inside a v-if statement.

 <div v-if="!isLoading"> 
   <GMap ref="mapRef" />
 </div>

I just changed that to v-show

<div v-show="!isLoading"> 
   <GMap ref="mapRef" />
 </div>

And now the object is available in mounted(). Still find it strange that the console.log(this.$refs) showed it being available as a key on this.$refs, even though it actually wasn't? Thats strange behaviour.

The other wierd thing was, that even if I tried to access this.$refs.mapRef in my data loading section, AFTER THE DATA WAS LOADED, (ie after isLoading=false), I still couldn't access it. Even though then, it should've been available because the v-if passed.

So v-show solved it, by just hiding the div, instead of not rendereding it. Stupid little workaround.


Main solution is to make sure your component is not imported dynamically

You'd still need to also avoid wrapping your component with v-if and use this.$refs.mapRef in mounted(), instead of created()

I wasn't using Vue Google Maps plugin/component, but my own custom component


Method 1

Use:

import CustomComponent from './CustomComponent ';

export default {
  components: {
    CustomComponent,
  },
  ...
}

Instead of

export default {
  components: {
    CustomComponent: () => import('./CustomComponent'),
  },
  ...
}

Method 2

Use this.$refs.mapRef in mounted() instead of created().

This way, setTimeout() and this.$nextTick() not needed

Correct way:

mounted(){
  //works! child components were loaded
  console.log(this.$refs.mapRef) 
}

Wrong way:

created(){
  //DON'T DO THIS! child components hasn't loaded
  console.log(this.$refs.mapRef) 

  //might work but it's an unreliable workaround
  setTimeout(()=>{
    console.log(this.$refs.mapRef)
  }, 0);

  //might work but it's an unreliable workaround
  this.$nextTick(()=>{
    console.log(this.$refs.mapRef)
  });

}

Method 3

Do not wrap child component using v-if, use v-show instead.

Correct way:

 <div v-show="!isLoading"> 
   <GMap ref="mapRef" />
 </div>

Wrong way:

 <div v-if="!isLoading"> 
   <GMap ref="mapRef" />
 </div>

Other Methods (not proper & not a solution)

The following are methods I tried but didn't work until I used the methods on top (stopped using dynamic import)

I already put this.$refs.mapRef in mounted() tried to wrap it with setTimeout() and 2 layers of this.$nextTick().

It works only on hot reload, but no longer works after a page refresh

mounted(){
  setTimeout(()=>{
    this.$nextTick(()=>{
      this.$nextTick(()=>{
        console.log(this.$refs.mapRef) // returns undefined
      });
    });
  }, 0);
}

Thanks to this comment in another answer: How to access to a child method from the parent in vue.js


I had a similar problem getting a ref to a leaflet map instance, try waiting for the "nextTick"

mounted(){
  this.$nextTick(()=>{
    let self = this
    console.log(self.$refs) // Shows the mapRef object reference
    console.log(self.$refs.mapRef) // returns undefined ???
  });
}

see the docs for more- https://v2.vuejs.org/v2/api/#vm-nextTick and https://v2.vuejs.org/v2/api/#mounted