sveltesvelte-3svelte-componentrecursive-component

svelte:self tag is not rerendered properly


so I've been trying to use the <svelte:self /> tag to create some recursive behaviour, in order to build something like a file browser. But it seems like the tag is not triggered by an update event or something.

Firstly, I have a parent component that wraps the actual recursive and important one. The parent is used to define the dimensions of the browser component and pass it the necessary data, that shall be fetched from a database in future iterations.

Parent.svelte

<script>
    import RecursiveChild from 'RecursiveChild.svelte';

    const data: any = [
        {
            label: 'general settings',
            options: [
                {
                    label: 'test',
                },
                {
                    label: 'abc',
                },
            ],
        },
        {
            label: 'orders',
            options: [
                {
                    label: 'ladida',
                },
                {
                    label: 'haha',
                },
            ],
        },
    ];
</script>

<div class="main">
    <RecursiveChild paths={data} />
</div>

<style lang="scss">
    @import '../lib.scss';

    .main {
        @include fill();

        display: flex;
    }
</style>

Then I have my recursive child component, where I used the <svelte:self /> tag that shows the weird behaviour.

RecursiveChild.svelte

<script>
    export let paths;

    let labels = paths ? paths.map((value) => value.label) : [];

    let pressedLabel: number;
    function handleClick(i: number) {
        pressedLabel = i;
    }
</script>

<div class="paths">
    {#each labels as label, i}
        <div class="label" on:click={() => handleClick(i)}>{label}</div>
    {/each}
</div>

{#if pressedLabel !== undefined}
    <svelte:self paths={paths[pressedLabel].options} />
{/if}

<style lang="scss">
    .paths {
        display: inline-flex;

        flex-direction: column;
    }

    .label {
        margin: 25px;
        background-color: green;
    }
</style>

I've uploaded a video file of the behaviour here. As you can see, once one has clicked on any of the labels, the other one won't work anymore. I cannot explain myself why this would be the case. The expected behaviour would be that once you click on general settings the labels test and abc show up. Then, when orders is placed, ladida and haha should show up. But as explained nothing happens and test and abc continue to be rendered.


Solution

  • The component pulls labels from the passed paths only once, so it will not react to any changes to the property. You have to declare any variables that should update as reactive via $:

    export let paths;
    
    $: labels = paths ? paths.map((value) => value.label) : [];