Subscribe on changes!

slots are not reactive in setup (but are in template via $slots)

avatar
Sep 17th 2021

Version

3.2.11

Reproduction link

github.com

Steps to reproduce

  • Run repro: yarn && yarn dev
  • Click "Toggle footer component"
  • Observe difference between Happy and Sad components

What is expected?

$slots.NAME and computed(() => !!slots.NAME) should function the same for detecting slot changes

What is actually happening?

Only $slots.name works, slots in the setup context doesn't appear to trigger reactivity.

avatar
Sep 17th 2021

$slots isn't reactive either and never has been. This would also not work and never worked in Vue 2 as well:

<script>
export default {
  computed: {
    hasFooter() {
      return !!this.$slots.footer
    }
  }
}
</script>
<template>
  <div>
    <div>
      <slot />
    </div>
    <div>
      <slot name="footer" />
    </div>
    <p v-if="hasFooter">I have detected the footer slot!</p>
  </div>
</template>

What happens is that the change of the slot content makes the component re-render, and during re-render, $slot.footer is being evaluated in the template.

You can use a simple function instead of a computed if you don't want to rely on $slots in the template:

<template>
  <div>
    <div>
      <slot />
    </div>
    <div>
      <slot name="footer" />
    </div>
    <p v-if="hasFooter()">I have detected the footer slot!</p>
  </div>
</template>

<script>
import { computed } from 'vue'

export default {
  setup(_, { slots }) {
    const hasFooter = () => !!slots.footer

    return { hasFooter }
  }
}
</script>
avatar
Sep 17th 2021

Thanks for the explanation and workaround @LinusBorg - sorry for the bad issue report!