Skip to content
Advertisement

Svelte UI not properly updated when Writable of Type Array is edited

I am currently working on a crew creator for a rowing club for context.

The Writable containing the array is declared as such

import { writable, Writable } from 'svelte/store';
import type { CrewMember } from '../types';

export default writable<Writable<CrewMember>[]>([], () => {
    console.log('Subscriber Detected ON CREW MEMBERS');

    return () => console.log('No More Subscribers ON CREW MEMBERS');
});

This is how im removing the specific item from the array

members.update((currentList) => {
    let tmp = currentList.filter((val) => {
        if (val != mem) return true;
    });

    return tmp;
});

Where mem is the Writable<CrewMember> passed into the delete function. I’ve also tried deleting by ID and that has not worked as of yet either.

The code is removing the Item from the list just fine however it’s not correctly updating the UI, always removing the last item of the table as opposed to the deleted item, this does get fixed when the page is refreshed since they’re pushed to a DB.

Would it be because I’m using a Writable<Writable<CrewMember>[]> because even I know that it sounds pretty damn cursed.

the following is the entire code block that is causing the problem

<script lang="ts">
    import { onDestroy, onMount } from 'svelte';
    import { get } from 'svelte/store';
    import type { Writable } from 'svelte/store';
    import type { CrewMember } from '../../types';

    import CrewMemberListItem from './CrewMemberListItem.svelte';

    export let members: Writable<Writable<CrewMember>[]>;
    export let pushToBuffer: { (mem: Writable<CrewMember>): void };

    let unsubscribe: { (): void };

    function deleteMember(mem: Writable<CrewMember>) {
        let member = get(mem);

        if (!confirm(`Are you sure that you want to delete ${member.name}?`))
            return;

        member.delete().then(() => {
            members.update((currentList) =>
                currentList.filter((val) => val != mem)
            );
        });
    }

    onMount(() => {
        unsubscribe = members.subscribe(
            (_updatedValue: Writable<CrewMember>[]) => {
                // internalMembersBuffer = updatedValue;
            }
        );
    });

    onDestroy(() => {
        unsubscribe();
    });
</script>

<table id="member-editor-list">
    <tr class="header">
        <th>Gender</th>
        <th>Age Group</th>
        <th class="left">Name</th>
        <th>Actions</th>
    </tr>

    {#each $members as member}
        <CrewMemberListItem
            {member}
            {pushToBuffer}
            deleteFunction={deleteMember}
        />
    {/each}
</table>

for simplicity sake, the css is removed

Advertisement

Answer

Thankyou to @pilchard for reminding me of the existence of the keyed each block. Which is what I should’ve been using.

no logic change was needed however the each block should now look like this

{#each $members as member (get(member).id)}
    <CrewMemberListItem
        {member}
        {pushToBuffer}
        deleteFunction={deleteMember}
    />
{/each}

as opposed to this

{#each $members as member}
    <CrewMemberListItem
        {member}
        {pushToBuffer}
        deleteFunction={deleteMember}
    />
{/each}

it appears the (get(member).id) is helping svelte to realise that these are different enough that is can’t just change the UI properly by looking at the lists length.

my testing has shown that this works as required. Tip for my future self, RTFM 🙂

again, thank you to @pilchard

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement