javascriptvuejs3vue-composition-apivue-script-setupvuejs-slots

How to clear "Slot 'default' invoked outside of the render function" warning? Vue3, Composition API


The full warning message:

[Vue warn]: Slot "default" invoked outside of the render function: this will not track dependencies used in the slot. Invoke the slot function inside the render function instead.

Nothing is broken and the functionality runs fine. I am just trying to clear this error.

Here is my code:

<template>
  <ul class="flex" data-test-id="tab-wrapper">
    <li
      v-for="(tab, index) in tabs"
      :key="index"
      @click="currentTab = tab"
    >
      {{ tab }}
    </li>
  </ul>
  <slot />
</template>


<script setup>
import { provide, ref, useSlots } from "vue";

const slots = useSlots();
const tabs = (slots.default && slots.default().map(tab => tab?.props?.title)) ?? [];
const currentTab = ref(tabs[0]);

provide("currentTab", currentTab);
</script>

It is my understanding that render() is implicit in the template, but I don't know how to implement the way I am using slots in the template v-for.

Other questions that I have seen about this involve setup(props, context) or actually rendering the template using render() inside the script tag.

I'm brand new to Vue3/Composition API so I'm sure I did something wrong, but I'm having trouble figuring out exactly what. Any help is appreciated!


Solution

  • UPDATE

    I've done it using render functions, just for sake of trying.

    I would not do it this way in real, since it's too complicated and feels very like bad designed.

    const { createApp, ref, h, resolveComponent } = Vue;
    
    const Tab = {
      props: ['title'],
      template: 'Tab: {{ title }}'
    }
    
    const Tabpane = {
      components: { Tab },
      setup(props, { slots, expose }) {
        const currentTab = ref(null);
        const tab = resolveComponent('Tab')
        return () => h('div', null, 
          [
            h('div', null, 'Current Tab: ' + (currentTab.value ? currentTab.value.props?.title : '') ),
            h('ul', { class: 'flex', 'data-test-id' : 'tab-wrapper' }, 
             slots.default()?.map(tab => 
              h('li', 
                { onClick: () => { currentTab.value = tab } }, 
                tab.props.title)) ?? []          
            ),
            currentTab.value ? h(tab, 
              { title: currentTab.value?.props.title }, 
              currentTab 
            ) : null
         ])
      }
    }
    
    const App = { 
      components: { Tabpane }
    }
    const app = createApp(App)
    app.mount('#app')
    #app { line-height: 2; }
    [v-cloak] { display: none; }
    li { cursor: pointer; text-decoration: underline; color: blue;}
    <div id="app" v-cloak>
    <tabpane>
    <tab title="One"></tab>
    <tab title="Two"></tab>
    </tabpane>
    </div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
    <script type="text/x-template" id="tabpane">
      <ul class="flex" data-test-id="tab-wrapper">
        <li
          v-for="(tab, index) in tabs" :key="index" @click="currentTab = tab">
          {{ tab }}
        </li>
      </ul>
      <slot></slot>
    </script>


    This is a duplicate of the Vue 3 cli-service app: "Slot "default" invoked outside of the render function" warning when component with slots is imported from other component

    You should not use slots.default() outside the render() function.

    That's what the Vue warning says.