Vue 3 composition utilities to programmatically keep track of descendant components
To achieve better flexibility in terms of composition for creating compound components like DropDown
, Listbox
, Tab
and so forth, there is a common scenario: defining descendants in a non-predictable way as follows:
<List>
<ListItem /> // index 0
<ListItem /> // index 1
<div class="inner">
<div class="inner">
<ListItem /> // index 2
</div>
<ListItem /> // index 3
</div>
</List>
Defining compound components unlock the power of composition. This means that the descendant could be shown in any depth of the component tree. So we need a deterministic way to track the descendants properly.
yarn add use-deep-descendants
The use-deep-descendants
exposes two 2 different composition utilities. useDeepDescendants
and useRegisterDescendant
. For a complete working example, check out the playground.
the useRegisterDescendant
must be used in descendant component as follow:
<template>
<div :id="id" ref="ref" role="option">
<slot />
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
import { useRegisterDescendant } from 'use-deep-descendants';
export default defineComponent({
name: 'ListItem',
setup(props) {
const id = computed(() => {
return Math.random().toString(36).slice(2);
});
const { ref, index } = useRegisterDescendant({ id });
return { ref, index, id };
},
});
</script>
This utility itself returns a ref and the index. The "index" covers the most certain cases. But additionally, for better accessibility support in some scenarios, you could pass any data to the utility, this data could be accessed in the parent component.
the useDeepDescendants
must be used in the parent component as follow:
<template>
<div>
<slot />
</div>
<pre>{{ descendants }}</pre>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useDeepDescendants } from 'use-deep-descendants';
export default defineComponent({
name: 'List',
setup() {
const { descendants } = useDeepDescendants();
return { descendants };
},
});
</script>
This utility itself returns the registered descendants.