recursionsveltesveltekitsvelte-3

Why <slot item={child} /> is needed for the nested object to be displayed if <slot item={item} /> is present?


This question is in reference to

Pass in a slot to a recursive component

REPL

App.svelte

<script>
    import Tree from './Tree.svelte';

    const items = [
        {
            type: 'person', first: 'Maru', last: 'Doe',
            children: [
                { type: 'person', first: 'Pochi', last: 'Doe' },
                { type: 'person', first: 'Tama', last: 'Doe' },
                { type: 'thing', manufacturer: 'ACME', product: 'missiles' },
            ]
        }
    ]
</script>

<Tree {items} let:item>
    {#if item.type == 'person'}
        {item.last}, {item.first}
    {:else if item.type == 'thing'}
        {item.product} ({item.manufacturer})
    {/if}
</Tree>

Tree.svelte

<script>
    export let items;
    export let level = 0;
</script>

<div style:margin-left="{level * 20}px">
    {#each items as item}
        {(console.log({item}),'')}  ---> all items are shown here
        <slot item={item} />
        <svelte:self items={item.children ?? []} level={level + 1}
                                 let:item={child}>
            <slot item={child} />
        </svelte:self>
    {/each}
</div>

In Tree.svelte, if we remove <slot item={child} /> only the parent item is shown i.e. Maru Doe.

But we have the following line(in Tree.svelte) <slot item={item} /> and if we console print item we get all the items i.e. Pochi, Tama, ACME. Should <slot item={item} /> not pass item to the parent (App.svelte) for the items to be printed in each recursion?

So, why are these not displayed when removing <slot item={child} />? When is <slot item={child} /> called? Is it like this, svelte:self calls itself recursively and when the last child object is reached, <slot item={child} /> is called?


Solution

  • It is necessary for recursion.

    If the <svelte:self> does not have any content (regardless of whether that is a <slot> or something else), it will not know how to display the items as that is passed onto <slot> within the component.

    So if that is not set, the upper <slot> definition is used to render the items at the current level, but for any children of these items, nothing will be specified.

    <slot item={child} /> is not just used for some specific item, it is used for all items that are passed in via <svelte:self items={item.children ?? []} ....
    In the template that is this:

    {#each items as item}
        <slot item={item} />
    

    You could think of it as a function like this, maybe that helps:

    function tree(items, render) {
        for (const item of items) {
            render(item);
    
            // `render` needs to be passed again
            tree(item.children ?? [], render);
        }
    }