In this post, we learn how to use Svelte Keyed Each Block.
As you might know from our previous post on Svelte Each Block, we use the each keyword to render an array or list. While it works when we have static lists, there is a serious issue when we delete items from the list. We are going to look at the issue and the solution to the same in this post.
1 – Svelte Each Block Issue
To demonstrate the issue, we will take the example of displaying books and their respective authors.
Below is our App component.
<script>
import Book from "./Book.svelte"
let books = [{
bookName: "Eye of the World",
},
{
bookName: "The Way of Kings",
},
{
bookName: "The Name of the Wind",
}]
function handleClick() {
books = books.slice(1);
}
</script>
<h1>Welcome to the Fantasy Library</h1>
{#each books as book, idx}
<h3>{idx + 1}</h3>
<Book
bookName = {book.bookName}
/>
{/each}
<button on:click={handleClick}>
Remove first Book
</button>
Here, we have the Book array with three books. We use the each block to render the books using the Book component. However, now we have a button to remove first book from the block. When the user clicks the button, we invoke the handleClick() function that slices the book array.
Below is the Book component.
<script>
const authors = {
"Eye of the World": "Robert Jordan",
"The Way of Kings": "Brandon Sanderson",
"The Name of the Wind": "Patrick Rothfuss",
}
export let bookName;
const author = authors[bookName];
</script>
<div>
<span>Book Name: {bookName} // Author: {author}</span>
</div>
In this component, we have the book to author map. Based on the input bookName, we basically determine the author name.
If we run the app now and then click the button once, we will see the below output.
As you can see, the bookName and the authorName does not match. Though the first book was correctly deleted, the authorName got mixed up.
Why does this happen?
The reason is that when we modify the value of an each block, Svelte adds and removes items from the end of the block. It also updates any values that have changed. This is the default behaviour.
Due to this, the last item is removed and then, the items above it are updated. However, the author being a local constant of the component is not updated leading to the mismatch. It was fixed during component initialization.
2 – The Svelte Keyed Each Block
To get around this issue, we need to use keyed each block.
See below example:
<script>
import Book from "./Book.svelte"
let books = [{
id: 1,
bookName: "Eye of the World",
},
{
id: 2,
bookName: "The Way of Kings",
},
{
id: 3,
bookName: "The Name of the Wind",
}]
function handleClick() {
books = books.slice(1);
}
</script>
<h1>Welcome to the Fantasy Library</h1>
{#each books as book, idx (book.id)}
<h3>{idx + 1}</h3>
<Book
bookName = {book.bookName}
/>
{/each}
<button on:click={handleClick}>
Remove first Book
</button>
Basically, here we introduce an id field in the books array. Each book has a unique id. In real life application, this id could also be the database id. The point is that it should be unique for every item.
We also utilise this id in the each block as below.
{#each books as book, idx (book.id)}
Basically, the role of this id is to help Svelte figure out which DOM node should be changed when the each block updates. After this change, if we run the application and click the button, we should see proper data as below.
The book name and author name match. The first book was removed. However, Svelte only removed the appropriate DOM node.
Conclusion
With this, we have successfully learnt how to use Svelte Keyed Each Block. This is extremely useful when we have a requirement to update elements within our each blocks.
If you have any comments or queries about this post, please feel free to write them in the comments section below.
0 Comments