In this post, we learn how to use Svelte Keyed Each Block.
In the normal Svelte Each Block, we use the each
keyword to render an array or list. While it works perfectly when we have static lists, there is a serious issue when we delete items from the list. We are going to first look at the issue and then come up with the solution to it.
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.
App.svelte
<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>
In the above piece of code, we have the books
array with three hard-coded books. We use the each
block to render the books
using the Book
component. Finally, we also have a button to remove the first book from the each
block. When the user clicks the button, we invoke the handleClick()
function that slices the books
array.
Below is the code for the Book
component.
Book.svelte
<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 name of the author.
If we run the app now and then click the button to remove first book 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 underlying array 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 the items above it are updated. However, the author
value is not updated because it is the local constant of the Book
component. Its value was fixed at the time of component initialization. This leads to the data mismatch.
2 – The Svelte Keyed Each Block
To get around this issue, we need to use keyed each block.
See below example:
App.svelte
<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 a 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
field is to help Svelte figure out which DOM node should be changed when the each
block updates. After this modification, if we run the application and click the button, we will see proper data as below.
The book name and author name match as expected. The first book was successfully removed. However, Svelte only removed the appropriate DOM node and other nodes were left as they were before the update.
Conclusion
With this, we have learnt how to use Svelte Keyed Each Block. This is extremely useful when we have a requirement to update elements within our each blocks.
Hope this little post was useful. If you have any comments or queries about this post, please feel free to write them in the comments section below.
Top comments (0)