async-awaitvuejs3async-components

How to wait for async component mounted in vue 3?


<template>
    <HelloWorld v-if="isShow" ref="comp"></HelloWorld>
    <button @click="show">Show</button>
</template>
<script>
import { defineAsyncComponent, ref, nextTick } from 'vue'
export default {
    components: {
        HelloWorld: defineAsyncComponent(() => import('./HelloWorld.vue))
    },
    setup() {
        const isShow = ref(false)
        const comp = ref(null)
        const show = async () => {
            isShow.value = true
            await nextTick()
            console.log(comp.value) // return null
            
            // this works
            setTimeout(() => {console.log(comp.value)}, 300)

        }
        return {isShow, comp, show}
    }
}
</script>

I want to access HelloWorld component after mounted but console log show that comp.value is null.

It works when I use setTimeout for 300ms instead of nextTick but I don't want to use that approach. What should I do?


Solution

  • Here is the way I see it if you want to avoid using setTimeout. First I apologize in advance, my solution is in Composition API I'm more comfortable with this syntax. I hope it will help you still.

    You need to create an emit that will be trigger when the component is mounted. This way, when the emit is trigger, your comp.value is initialized and mounted.

    Parent.vue :

    <script setup lang="ts">
      import { defineAsyncComponent, ref } from 'vue';
    
      const HelloWorld = defineAsyncComponent(() => import('./HelloWorld.vue'));
    
      const isShow = ref(false);
      const comp = ref(null);
      const show = async () => {
        isShow.value = true;
      };
    
      const consoleComp = () => {
        console.log(comp.value);
      };
    </script>
    
    <template>
      <HelloWorld
        v-if="isShow"
        ref="comp"
        @mounted="consoleComp"
      />
      <button @click="show">Show</button>
    </template>
    

    HelloWorld.vue :

    <script setup lang="ts">
      import { onMounted, ref } from 'vue';
    
      const greeting = ref('Hello, world!');
    
      const emit = defineEmits(['mounted']);
    
      onMounted(async () => {
        emit('mounted');
      });
    </script>
    <template>
      <div>
        <h1>{{ greeting }}</h1>
      </div>
    </template>
    

    Hope it helps you ! :)