When having an #each block in svelte (like https://learn.svelte.dev/tutorial/keyed-each-blocks), the entry is only updated if the content changed. This works perfectly in the tutorials example, where a string is given as property to a nested component:
{#each things as thing (thing.id)}
<Thing name={thing.name}/>
{/each}
But if I give the whole object (thing
) and adjust Thing accordingly, it always updates all list entries. Hence I wonder what the condition is Svelte decides on, whether to update the component or not. Is it the property, which is incase of the whole object a reference and therefore always changes? Or is the whole Nested component generated to be compared against the DOM? Is it bad practice to give an Object to a component?
App.svelte
<script>
import Thing from './Thing.svelte';
let things = [
{ id: 1, name: 'apple' },
{ id: 2, name: 'banana' },
{ id: 3, name: 'carrot' },
{ id: 4, name: 'doughnut' },
{ id: 5, name: 'egg' }
];
function handleClick() {
things = things.slice(1);
}
</script>
<button on:click={handleClick}>
Remove first thing
</button>
{#each things as thing (thing.id)}
<Thing name={thing} />
{/each}
Thing.svelte
<script>
import {
beforeUpdate,
afterUpdate
} from 'svelte';
const emojis = {
apple: '🍎',
banana: '🍌',
carrot: '🥕',
doughnut: '🍩',
egg: '🥚'
};
export let name;
const emoji = emojis[name.name];
beforeUpdate(() => {
console.log('before updating ' + name.id)
});
afterUpdate(() => {
console.log('after updating ' + name.id)
});
</script>
<p>{emoji} = {name.name}</p>
The update lifecycle functions are called everytime, even if the content didn't change.
Edit: With the REPL there is this JS output tab which I searched a little. There are many of these p() {...} like:
p(ctx, [dirty]) {
if (dirty & /*name*/ 1 && t2_value !== (t2_value = /*name*/ ctx[0].name + "")) set_data(t2, t2_value);
},
which seem to do the job. The one above is the one from the Thing
create_fragment
return. To me, the comparison seems good, but still an update is done.
You have a couple of questions in your post, so to best help, I'll break them down into separate parts. I hope this clears things up at least a little bit.
Is it the property, which is incase of the whole object a reference and therefore always changes? Or is the whole Nested component generated to be compared against the DOM?
It sounds like you are on the right track. This is how Svelte's reactive behavior works. It will trigger an update when the props are determined as changed; however, it does not do a deep equality check. So in your example, you are passing in a whole object, so the reference to the object will be used to determine if the prop has changed.
In the {#each}
block, you have set {thing.id}
as the key. This means that Svelte will follow these rules (for the most part) to determine whether it should rerender the component:
thing.id
changes.So here comes the tricky part. Even though you are only slicing out part of the array in handleClick()
Svelte will still update the component since it sees that the reference has changed.
Note: Creating a new array with new references is the behavior of slice
.
To get around this, you could pass the component a specific property of the object rather than the entire thing:
{#each things as thing (thing.id)}
<Thing name={thing.name}/>
{/each}
Is it bad practice to give an Object to a component?
This is a bit subjective, but in my opinion, passing an object is not necessarily a bad practice, but you should be aware of the implications. A couple of high-level points off the top of my head might be:
But like I said, this is only my opinion.
p(ctx, [dirty])
and what is going on hereThe update lifecycle functions are called every time, even if the content didn't change.
As I'm sure you know, this is a part of Svelte's compiled code. This checks if the name
prop in the ctx
object (current context of the component) has changed, and if so, it will update the text content of the corresponding DOM element. In your case, the name
prop is an object, and every time you slice the array, that object's reference changes, so this function will always consider it as "changed".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With