Svelte user interface fails to update correctly after editing a writable array type

Currently, I am working on developing a crew creator tool for a rowing club to provide some context.

The Writable container that holds the array is defined as follows


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 I am removing a specific item from the array

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

    return tmp;
});

Where mem represents the Writable<CrewMember> passed into the delete function. I have also attempted deleting by ID without success so far.

The issue lies in the fact that while the code effectively removes the item from the list, it does not update the UI correctly. It consistently removes the last item in the table instead of the intended deletion, although this behavior is rectified upon page refresh since changes are pushed to a database.

Could the problem stem from the use of

Writable<Writable<CrewMember>[]>
because even I acknowledge that it sounds somewhat unusual.

The following showcases the complete problematic code block

<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>

To simplify, the CSS has been excluded

Answer №1

Big shoutout to @pilchard for bringing back the keyed each block to my attention. That's what I should've been using all along.

No need for any logic changes, but now the each block should look like this:

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

Instead of this:

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

Adding (get(member).id) seems to be the key in making Svelte understand that these lists are distinct enough to not just rely on length when updating the UI.

My testing confirms that this new approach works perfectly. Note to self: Always read the manual! :)

Once again, huge thanks to @pilchard!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Please enter a number using the "+" sign and a decimal point

In my input field, I have specified: <input type="number" step="0.01"> I expect all of these input values to produce the following outputs: 2.00 => 2.00 2,00 => 2.00 +2,00 => 2.00 However, when entering the value "+2.00", it fail ...

Making a dynamic accordion using JavaScript

I am currently coding in Javascript, without utilizing jQuery. Progress so far on the accordion title panel: var accordion = document.createElement('div'); accordion.className = 'panel-group'; accordion.id = 'accordion'; doc ...

What is the best way to utilize the "useRouter" function to extract parameters from the URL within the getServerSideProps() method in Next.js?

How can I access the URL parameters within the getServerSideProps() method? Below is the snippet of my code. The objective of my code is to extract parameters from the URL, fetch data from the API, and render it on the front end. import { useRouter } from ...

Introduce a brief 7-second pause prior to triggering the customized modal in the JavaScript variable

Is there a way to make the below variable activate after waiting for 7 seconds? My attempt at chaining didn't work. $(function(){ var inst = $.remodal.lookup[$('[data-remodal-id=modal]').data('remodal')]; inst.open(); }); ...

"Learn how to convert basic input fields into user-friendly Bootstrap input groups using the power of jQuery

Is there a way to use jQuery to convert my basic input field into a bootstrap input-group like the one shown below? This is how I created the input: <div class='input-group date' id='ff'> <input type='text' class= ...

Explore the intricacies of complex hierarchies

<table id="financial101_tab1" class="dxrpControl_Moderno dxrpWithoutHeader_Moderno"> <tbody> <tr> <td id="financial101_tab1_RPC" class="dxrp dxrpcontent"> <input id="BlockControlfinancial101_tab1ATI" type= ...

Adjusting the dimensions of a div to accommodate varying text lengths

I'm currently facing an issue where I have multiple divs all sharing the same "Text" class. These divs don't have set width or height values, and are adjusting based on the content inside them, which varies in width. The problem arises when other ...

Tips for retrieving the ID value of the <li> element using JavaScript and AJAX

Here is a snippet of code I've been using to send the value of an <option>: function getXhr() { var xhr = null; if(window.XMLHttpRequest) // Firefox et autres xhr = new XMLHttpRequest(); else if(window.ActiveXObject){ // I ...

What is the best way to implement a hover effect on multiple rows within an HTML table using Angular?

I am currently working on developing a table preview feature to display events. I previously sought assistance here regarding positioning elements within the table and successfully resolved that issue. Applying the same principles, I am now attempting to c ...

Tips for checking the validity of PHP variable names, such as $as['abc'], within an HTML textbox

Can anyone assist me with validating a user input PHP variable name such as $as_cap['abc'] during insertion? I need to verify if the format of the variable name is correct or incorrect. Currently, I am using eregi("^[a-z0-9_.'-]{1,50}$") ...

The module "angular2-multiselect-dropdown" is experiencing a metadata version mismatch error

Recently, I updated the node module angular2-multiselect-dropdown from version v3.2.1 to v4.0.0. However, when running the angular build command, I encountered an "ERROR in Metadata version mismatch for module". Just to provide some context, I am using yar ...

What could be causing the malfunction in this async iterator for readline?

Condensing a larger process into a minimal reproducible example in node v14.4.0, the issue arises where nothing is outputted from within the for loop. The only console output observed is: before for() loop finished finally done The for await (const line1 ...

What is the best way to dynamically adjust the size of a table or div when clicking in Angular 2?

Is there a way to dynamically resize a table or div in Angular 2 by clicking on a button or other element? I am attempting to change the width of a table from 300px to 100px using a function that utilizes CSS and TypeScript. table{ width: 300px; res ...

How do @material-ui/core and @types/material-ui interact with each other?

Exploring a sample project that utilizes material-ui. Upon inspecting the package.json file, I noticed the inclusion of the following packages: { ... "dependencies": { "@material-ui/core": "^1.4.1", ... }, "devDependencies": { "@types ...

I am in search of the corresponding code for Delphi 5/6

Below is a code snippet that converts a String Array to a Byte Array. I am currently working on using a WebService to retrieve a binary file. The WebService returns the content of the file as a string, which I then need to convert to a byte array. I have m ...

Error Encountered: Unexpected Identifier in Angular 7 External jQuery Plugin

Struggling to convert a jQuery template to Angular7, I'm facing an issue with loading .js files from the assets folder in the original template to make it functional. Upon starting the application with: ng serve, I encounter the following error in th ...

When I try to enlarge a fractal in a WebGL rendering, I run into a problem

When I zoom in excessively and attempt to move the picture with my mouse, it moves too quickly. Conversely, when I zoom out significantly, the picture moves very slowly. This refers to the scaling during zooming and how the position of the picture is adju ...

Guide to waiting for API responses with redux-saga

I have a React-Typescript app with backend calls using React Saga. I'm facing an issue where when one of my frontend functions makes a backend call, the next function starts executing before the previous one finishes. Currently, I'm using the SE ...

Tips for updating a div element while maintaining its style and select2 plugin functionality with jQuery

Within the HTML code, I am attempting to refresh the following div: <select name="test[]" id="test" multiple required class="select2"> @foreach($tests as $s) ...

Most efficient method to upload numerous images without any lag

I have a website where images are loaded only when they are slightly below the viewport. This method allows the site to load initially without images and then determine which ones need to be loaded based on the user's viewpoint. When a user scrolls ...