Search code examples
vuejs3vue-composition-api

How to access instance in vue3 composition API lifecycle hooks


I stumbled into a totally unexpected problem while refactoring my code to composition API: there doesn't seem to be any (documented) way of accessing current instance from the lifecycle hooks.

sample code:

import { defineComponent, onMounted } from 'vue';

export default defineComponent({
  setup() {
    onMounted(() => { 
      console.log(this);  // <-- will be undefined
    });
  },
  mounted() {
    console.log(this);  // <-- will be the component
  },
}

I've spent hours trying to find a solution to this and ultimately just used the old options API to get what I want. None of examples, tutorials or documentation - that I read - use this in the hooks.

But I find it unbelievable that only undocumented getCurrentInstance would be the way to get the current instance from the hook.

So, which doc did I miss?


Solution

  • UPDATE 2.

    You can mix Composition API and Options API to access this, if you need it.

    Using this in Options API you can access all objects defined inside setup like variables, methods, computed, refs and etc.

    But you cannot access anything defined using Options API inside setup.

    The data provided over Options API is mixed with the data from setup. The objects from setup overwrite the definitions provided over Options API.

    See how Vue mixes the data properties in the following example.

    const { createApp, ref } = Vue;
    
    const MyComponent = {
      setup() {
        // there is no way to access the `optionsVar` inside the `setup`
        const setupVar = ref('setupVar content')
        const collisionVar = ref('content from setup')
        const collisionObj = ref({ setup: true })
        return { setupVar, collisionVar, collisionObj }
      },
      data() {
         return {
            optionsVar: 'optionsVar content',
            collisionVar: 'content from data()',
            collisionObj: { options: true } 
         }
      },
      methods: {
        log() {
           console.log(`setupVar: ${this.setupVar}`)
           console.log(`collisionVar : ${this.collisionVar}`)
           console.log(`collisionObj: ${JSON.stringify(this.collisionObj)}`)
        }
      },
     template: `<button type="button" @click="log()">log()</button>`  
    }
    
     const App = {
      components: {
        MyComponent
      }
    }
    
    const app = createApp(App)
    app.mount('#app')
    <div id="app">        
       <my-component />
    </div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>

    UPDATE 1.

    Here is the same example with a component

    const { createApp, ref, onMounted } = Vue;
    
    const MyComponent = {
      setup() {
        const id = ref(Math.round(Math.random() * 100000));        
        const count = ref(0);        
        const plus = () => { count.value++; }        
        const minus = function() { count.value--; }        
        
        onMounted(() => { 
          count.value = Math.round(Math.random() * 10)
        });    
        
        return {id, count, plus, minus }
      },
      template: `id: {{id}} &nbsp; <button type="button" @click="minus()">-1</button>
        &nbsp;{{count}}&nbsp;
        <button type="button" @click="plus()">+1</button><hr/>`   
    }
    
    const App = {
      components: {
        MyComponent
      }
    }
    
    const app = createApp(App)
    app.mount('#app')
    <div id="app">        
       <my-component v-for="i in 5" />
    </div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>

    What for do you need this in the component?

    If you create your component with Composition API, then you can access all the properties directly, without using this.

    Here is a very basic example:

    const { createApp, ref, onMounted } = Vue;
    
    const App = {
      setup() {    
    
        const count = ref(0);        
        const up = () => { count.value++; }        
        const down = function() { count.value--; }    
        
        onMounted(() => { 
          count.value = 10
        });    
        
        return {count, up, down }
      }
    }
    
    const app = createApp(App)
    app.mount('#app')
    <div id="app">        
        <button type="button" @click="down()">-1</button>
        {{count}}
        <button type="button" @click="up()">+1</button>
    </div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>