【问题标题】:Svelte.js - Can't Properly Delete Item From ListSvelte.js - 无法从列表中正确删除项目
【发布时间】:2019-11-28 01:53:47
【问题描述】:

我正在构建一个应用程序,用户可以在其中添加和删除不同“类型”的项目。例如,可能存在用户可以编辑文本的文本类型,以及用户可以放入图像的图像类型。

将项目添加到列表将导致列表更新并显示所有旧项目以及新项目。同样,删除项目将删除该项目并导致列表仅显示剩余项目。

添加功能运行良好。当项目属于不同类型时,删除会出现问题。这是我的(简化的)代码:

// List.svelte

<script>
    import {writable} from "svelte/store";
    import Wrapper from "./Wrapper.svelte";

    const items = [
        {
            type: "text",
            value: "Test 1"
        },
        {
            type: "img",
            value: "https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif"
        }
    ];

    const listStore = writable(items);
</script>

{#each $listStore as item, i}
    <Wrapper item={item} store={listStore} myIndex={i} />
{/each}
// Wrapper.svelte

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

    export let item;
    export let store;
    export let myIndex;

    let DynamicItem;

    const handleDeleteItem = () => {
        store.update((items) => ([...items.slice(0, myIndex), ...items.slice(myIndex + 1)]))
    }

    onMount(async () => {
        let imported;

        if(item.type === "text") {
            imported = await import("./TextItem.svelte")
        } else {
            imported = await import("./ImgItem.svelte")
        }

        DynamicItem = imported.default;
    })
</script>

<svelte:component this={DynamicItem} data={item} />
<button on:click={handleDeleteItem}>Delete</button>
// TextItem.svelte

<script>
    export let data;
</script>

<div contenteditable="true" bind:textContent={data.value} />
// ImgItem.svelte

<script>
    export let data;
</script>

<img src="{data.value}" width="100px" height="100px" alt="img alt" />

当用户通过单击第一个删除button 从列表中删除TextItem 时,ImgItem 不再显示图像,而是仅将src 属性呈现为文本。

查看 HTML 输出时,在删除操作之前,它看起来像这样:

<body>
    <div contenteditable="true">Test 1</div>
    <button>Delete</button>
    <img src="https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif" alt="img alt" width="100px" height="100px">
    <button>Delete</button>
</body>

然后,它看起来像这样:

<body>
    <div contenteditable="true">https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif</div>
    <button>Delete</button>
</body>

所以看起来 Svelte 并没有真正删除 TextItem,而是将内容从下一个项目移到它的位置。

如何修复我的代码以便真正删除项目?目标应该是通过执行相同的操作来生成以下 HTML:

<body>
    <img src="https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif" alt="img alt" width="100px" height="100px">
    <button>Delete</button>
</body>

感谢任何帮助。谢谢

【问题讨论】:

    标签: javascript html svelte


    【解决方案1】:

    所以看起来 Svelte 并没有真正删除 TextItem,而是将内容从下一个项目移动到它的位置。

    是的,绝对是,这就是正在发生的事情。

    Svelte(如 React)仅在其性质明显改变时(例如标签名称不同)在给定位置重新创建元素。否则,认为它是相同的元素/组件,只是一些道具/状态发生了变化。

    为了提示框架元素的身份已更改,您必须使用key 功能。在 React 中,它是一个文字 key 属性。在 Svelte 中,仅在 {#each ...} 块 (for now?) 中支持键。语法如下(docs):

    {#each expression as name, index (key)}...{/each}
    
    # also works, I think it is an oversight in the docs currently
    {#each expression as name (key)}...{/each}
    

    key 的值对于每个项目必须是唯一的,它才能工作。通常你会使用排序的 id。但实际上,它可以是任何东西。如果给定位置的元素/组件的值发生变化,那么它将被重新创建。

    因此,在您的情况下,您可以通过使用项目类型作为键来解决您的问题,例如:

    {#each $listStore as item, i (item.type)}
        <Wrapper item={item} store={listStore} myIndex={i} />
    {/each}
    

    这解决了您所询问的问题,但我必须说这不是一个好主意。出于其他原因,您最好将真正的唯一 ID 添加到您的项目中并使用它们。使用上面的代码,相同类型的项目将继续共享 DOM 元素,这可能不是您想要的。例如,您可能会遇到 DOM 元素(如输入)的焦点或内部状态(值)问题。

    所以,这样的事情更好,可能是要走的路:

    <script>
        import {writable} from "svelte/store";
        import Wrapper from "./Wrapper.svelte";
    
        const items = [
            {
                id: 1,
                type: "text",
                value: "Test 1"
            },
            {
                id: 2,
                type: "img",
                value: "https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif"
            }
        ];
    
        const listStore = writable(items);
    </script>
    
    {#each $listStore as item, i (item.id)}
        <Wrapper item={item} store={listStore} myIndex={i} />
    {/each}
    

    【讨论】:

    • 你说得对。很长一段时间以来,我一直在寻找一种在 Svelte 中使用键的方法,因为我习惯于在 React 中使用它们来处理这种确切的情况。并且一直认为它一直盯着我的脸在文档中。你是救生员
    猜你喜欢
    • 1970-01-01
    • 2020-10-13
    • 1970-01-01
    • 2016-09-20
    • 2018-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-01
    相关资源
    最近更新 更多