Search code examples
componentssveltereactive-programmingsvelte-component

Reactive variable updated from parent and child component


I have a parent component (List) which displays a list of child components (Block). Nested blocks have a button that allows the user to add/remove them to the selection. The List component also contains a table were are listed all the selected Blocks: the user can unselect a Block there too.

My problem is that when unselected from the table#selection, the isSelected variable is obviously not updated

How can I dynamically update the isSelected variable inside the Block component? Am I handling the logic wrong?

Example reproduced here

List.svelte

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

    export let blocks = [];
    let selection = {};
    let isBlockSelected = (block) => selection.hasOwnProperty(block.id);

    function removeFromSelection(blockId) {
        const { [blockId]: _, ...rest } = selection;
        selection = rest;
        // 🛑 TODO make block aware that it has been unselected
    }

    function addToSelection(block) {
        selection = { ...selection, [block.id]: block };
    }

    function handleToggleSelection(event) {
        const { block } = event.detail;

        if (!isBlockSelected(block)) {
            addToSelection(block);
        } else {
            removeFromSelection(block.id);
        }
    }
</script>

<div>
    {#each blocks as block (block.id)}
    <Block {block} on:toggleSelection={handleToggleSelection} isSelected={isBlockSelected(block)}/>
    {/each}
</div>

<table id="selection">
    <tbody>
        {#each Object.entries(selection) as [id, meta]}
        <tr>
            <td>
                <a href="/page/{id}" target="_blank">{meta.title}</a>
                <button class="delete" aria-label="close" on:click={() => removeFromSelection(id)}></button>
            </td>
        </tr>
        {/each}
    </tbody>
</table>

Block.svelte

<script>
    import {createEventDispatcher} from "svelte";

    export let block;
    export let isSelected = false;

    const dispatch = createEventDispatcher();

    function toggleSelection(block) {
        dispatch('toggleSelection', { block });
        isSelected = !isSelected
    }
</script>

<div>
    {block.title}
    <button on:click={() => toggleSelection(block)}>
        {#if isSelected}
            Remove from selection
        {:else}
            Add to selection
        {/if}
    </button>
</div>

Solution

  • To make

    isBlockSelected(block)
    

    update when selection changes either add it as a parameter to the declaration and all calls

    const isBlockSelected = (block, selection) => selection.hasOwnProperty(block.id);
    

    or make the statement reactive

    $: isBlockSelected = (block) => selection.hasOwnProperty(block.id);