Search code examples
viewsveltedirty-checking

In Svelte, you can use `array = array` to mark the array as needing a refresh in the view?


That is, using

array = array;

to trigger the view to refresh with the current array?

For example, the following uses

cards = [...cards, {name}];

to "add" an entry to the array cards, but by assigning cards to a new array, and cards in the view will be refreshed.

sample on: https://svelte.dev/repl/3ed20e7bac2e4b65944c98485d9217b3?version=3.18.1
(just type in some names and the list below will update).

However, this is O(n²) when entering n names, because each time, the creation of a new array is O(n). If (1) the user is expected to enter a few or a dozen name at a time, it is not such a big problem, or (2) the user may type a name for a few seconds up to 20 seconds, then any delay of the UI for 0.2 seconds isn't a big problem when the entry is up to 1000 or 2000 names. (the user probably will be better to add to the server and database just in case of power outage anyways.)

But in case we don't want the O(n²) time, we could also use:

    cards.push({name});
    cards = cards;

this will trigger a refresh in the view. So Svelte doesn't do "dirty checking"? Is it true that compared to Angular, Vue, and React, they do dirty checking by actually converting the array to a string and check whether the array has changed?

sample: https://svelte.dev/repl/70cc3b08f6864ef387c691b8f126a7fd?version=3.18.1

Without the line cards = cards;, the list won't get updated in the view:

sample: https://svelte.dev/repl/25f41c51798d425e805fb4586a843363?version=3.18.1

So Svelte doesn't do actually dirty checking but use the cards = something; as a hint that cards has changed, even by cards = cards? So can we just use this as a general technique to mark something as dirty if we use array.push() so that the program can run faster? Could Svelte actually guess that cards has changed by detecting there is a cards.push() statement?

Update: it was found that by adding a key to each list item, cards = cards is not needed, and I am wondering why:

https://svelte.dev/repl/d78158ae54684bf28b8c2e9b527f1915?version=3.18.1

<ul>
  {#each cards as card, i (i)}
    <li>{card.name}</li>
  {/each}
</ul>

Solution

  • Assigning a variable to itself is a hint to the compiler that the relevant parts of the view need to be updated — see here for more information. It's designed to allow you to work with mutable objects and arrays where necessary for performance, but to nudge you towards using immutable data structures since that will generally result in fewer bugs.

    Detecting things like array.push(...) isn't really practical — it's much better to have a single, clear way of telling the compiler 'this has changed', and the assignment operator is that way.

    The reason it works with the keyed each block but not the unkeyed one is actually due to a bug — I've raised an issue: https://github.com/sveltejs/svelte/issues/4373