Svelte.js - 无法从列表中正确删除项目
Svelte.js - Can't Properly Delete Item From List
我正在构建一个应用程序,用户可以在其中添加和删除不同 "types" 的项目。例如,可能存在用户可以编辑文本的文本类型,以及用户可以放入图像的图像类型。
将项目添加到列表将导致列表更新并显示所有旧项目和新项目。同样,删除一个项目将删除该项目并导致列表只显示剩余的项目。
添加功能完美无缺。当项目属于不同类型时,删除会带来问题。这是我的(简化的)代码:
// 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>
感谢任何帮助。谢谢
So it looks like Svelte is not truly deleting the TextItem but moving the content from the next item into its place.
是的,绝对是这样。
Svelte(如 React)仅在元素的性质发生明显变化时才在给定位置重新创建元素,例如,如果标签名称不同。否则,认为是同一个元素/组件,只是改变了一些 props/state。
为了提示框架元素的标识已更改,您必须 key
功能。在 React 中,它是一个文字 key
属性。在 Svelte 中,键仅在 {#each ...}
块 (for now?). The syntax is the following (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 of sort。但实际上,它可以是任何东西。如果给定位置的元素/组件的值发生变化,则会重新创建它。
因此,在您的情况下,您可以通过使用项目类型作为键来解决您的问题,例如:
{#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}
我正在构建一个应用程序,用户可以在其中添加和删除不同 "types" 的项目。例如,可能存在用户可以编辑文本的文本类型,以及用户可以放入图像的图像类型。
将项目添加到列表将导致列表更新并显示所有旧项目和新项目。同样,删除一个项目将删除该项目并导致列表只显示剩余的项目。
添加功能完美无缺。当项目属于不同类型时,删除会带来问题。这是我的(简化的)代码:
// 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>
感谢任何帮助。谢谢
So it looks like Svelte is not truly deleting the TextItem but moving the content from the next item into its place.
是的,绝对是这样。
Svelte(如 React)仅在元素的性质发生明显变化时才在给定位置重新创建元素,例如,如果标签名称不同。否则,认为是同一个元素/组件,只是改变了一些 props/state。
为了提示框架元素的标识已更改,您必须 key
功能。在 React 中,它是一个文字 key
属性。在 Svelte 中,键仅在 {#each ...}
块 (for now?). The syntax is the following (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 of sort。但实际上,它可以是任何东西。如果给定位置的元素/组件的值发生变化,则会重新创建它。
因此,在您的情况下,您可以通过使用项目类型作为键来解决您的问题,例如:
{#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}